sscan/actors/
queue.rs

1//! # Provides the Scan Input Queue
2//!
3//! The [`Queue`] actor provides an input queue for files and raw data
4//! items. Any data item implementing [`DataItem`] can be enqueued for
5//! scanning.
6//!
7//! ## Interacting with the Input Queue
8//!
9//! [`Queue`] is an asynchronous actor, meaning it runs on its own
10//! independent thread and has full control over its own mutable state.
11//! Interaction with the queue is done through message passing.
12//!
13//! See the [`messages`] module to learn about the various types of
14//! messages that can be sent to the queue to interact with it, along
15//! with usage and code examples.
16//!
17
18pub mod data_item;
19pub mod error;
20pub mod messages;
21
22use super::lua_vm::{messages::RegisterUserApi, LuaVM};
23use crate::userscript_api::queue_api::QueueApi;
24use data_item::DataItem;
25use error::Error as QueueError;
26use kameo::{
27    actor::{ActorRef, WeakActorRef},
28    error::BoxError,
29    mailbox::unbounded::UnboundedMailbox,
30    Actor,
31};
32use std::collections::VecDeque;
33
34/// # The Global Scan Queue
35///
36/// This actor provides a global scan queue. Userscripts can enqueue
37/// files and other items implementing trait [`DataItem`]. The queue
38/// is used for efficiently sending input items to all scan engines.
39pub struct Queue {
40    /// A double-ended queue storing items implementing [`DataItem`]
41    items: VecDeque<Box<dyn DataItem>>,
42
43    /// Weak ref to the Lua virtual machine, for registering the API.
44    lua_vm: WeakActorRef<LuaVM>,
45}
46
47/// # [`Queue`] is an actor.
48///
49/// This means that the queue runs on its own thread and communicates
50/// with other Rust components via message passing. This allows the
51/// queue to run alongside other asynchronous subsystems while
52/// maintaining owned mutable state without locks.
53impl Actor for Queue {
54    type Mailbox = UnboundedMailbox<Self>;
55
56    /// On startup, register the userscript API and help topics.
57    async fn on_start(&mut self, queue: ActorRef<Self>) -> Result<(), BoxError> {
58        if let Some(lua_vm) = self.lua_vm.upgrade() {
59            let queue_api: QueueApi = QueueApi::new(queue.downgrade());
60            lua_vm.tell(RegisterUserApi::with(queue_api)).await?;
61            Ok(())
62        } else {
63            Err(Box::new(QueueError::NoLuaVm))
64        }
65    }
66}
67
68impl Queue {
69    /// Create a global scan queue.
70    ///
71    /// Spawns a new [`Queue`] actor with a default queue
72    /// size of `ZERO`. The queue will dynamically resize as needed.
73    ///
74    /// **Efficiency**: A queue of size zero will allocate very
75    /// frequently when items are enqueued. It is recommended to use
76    /// [`Queue::spawn_with_size()`] to choose a
77    /// reasonable starting capacity.
78    #[must_use]
79    pub fn spawn(vm: WeakActorRef<LuaVM>) -> ActorRef<Self> {
80        let actor: Queue = Self {
81            items: VecDeque::new(),
82            lua_vm: vm,
83        };
84        kameo::spawn(actor)
85    }
86
87    /// Create a global scan queue with given capacity.
88    ///
89    /// Spawns a new [`Queue`] actor with the provided starting capacity.
90    /// This is recommended over [`Queue::spawn()`] as the initial
91    /// capacity can be tuned to help avoid excessive allocations.
92    #[must_use]
93    pub fn spawn_with_size(vm: WeakActorRef<LuaVM>, capacity: usize) -> ActorRef<Self> {
94        let actor: Queue = Self {
95            items: VecDeque::with_capacity(capacity),
96            lua_vm: vm,
97        };
98        kameo::spawn(actor)
99    }
100}