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}