bothan_lib/
worker.rs

1//! Workers for asset data collection and information updates.
2//!
3//! This module defines traits and implementations for workers that monitor and update asset information.
4//! Workers are responsible for collecting data from various sources and updating asset information in the store.
5//!
6//! The module provides:
7//!
8//! - The [`AssetWorker`] trait which defines the common interface for all asset workers
9//! - Specialized worker implementations for different protocols and data sources
10//! - Error handling specific to asset workers
11//!
12//! # Worker Types
13//!
14//! The module includes various worker implementations:
15//!
16//! - REST-based workers in the [`rest`] module for HTTP/HTTPS API polling
17//! - WebSocket-based workers in the [`websocket`] module for real-time data streaming
18//!
19//! # Worker Lifecycle
20//!
21//! Workers follow a typical lifecycle pattern:
22//!
23//! 1. **Initialization**: Workers are built using their `build` method and configured with options
24//! 3. **Running**: Workers run continuously, collecting data from sources and updating the store
25//! 4. **Error Handling**: Workers should handle temporary failures gracefully with retries
26//! 5. **Shutdown**: Workers should support graceful shutdown when requested
27//!
28//! # Worker Storage
29//!
30//! Workers should use the [`WorkerStore`](crate::store::WorkerStore) wrapper to interact with the main store.
31//! This wrapper provides namespace isolation for each worker, ensuring that different workers
32//! can operate without conflicts. Each worker should use the WorkerStore to update asset
33//! information through the `set_asset_info` and `set_batch_asset_info` methods.
34//!
35//! # Extensibility
36//!
37//! While this module provides general-purpose implementations that cover common use cases,
38//! the [`AssetWorker`] trait is designed for extensibility. Users can create custom worker
39//! implementations by implementing this trait to support specialized data sources, protocols,
40//! or business logic beyond what's provided in the standard implementations.
41//!
42//! # Implementation
43//!
44//! When implementing a new worker type:
45//!
46//! 1. Implement the [`AssetWorker`] trait
47//! 2. Define appropriate configuration options via the associated `Opts` type
48//! 3. Implement the `build` method to create worker instances from configuration
49//! 4. Implement any protocol-specific functionality in the worker
50//! 5. Use [`WorkerStore`](crate::store::WorkerStore) to store asset information
51//!
52//! Workers are typically registered with a worker registry and managed by the system
53//! to handle data collection and updates in a coordinated way.
54
55use error::AssetWorkerError;
56
57use crate::store::Store;
58
59pub mod error;
60pub mod rest;
61pub mod websocket;
62
63/// The universal trait for all workers that collect and update asset info.
64///
65/// This trait defines the common interface that all asset workers must implement,
66/// allowing the system to interact with different types of workers in a uniform way.
67/// Each worker is responsible for monitoring specific assets and updating their information
68/// in the store.
69///
70/// Worker implementations should:
71/// - Collect data from sources using appropriate methods
72/// - Parse and validate the received data
73/// - Update asset information using the [`WorkerStore`](crate::store::WorkerStore)
74/// - Handle errors and implement retry mechanisms
75/// - Support graceful shutdown
76///
77/// # Required Methods
78///
79/// Implementors must define:
80/// * `name()` - Returns a unique identifier for the worker type
81/// * `build()` - Creates a new worker instance with the specified options and query IDs
82///
83/// # Worker Store Usage
84///
85/// Workers should use the [`WorkerStore`](crate::store::WorkerStore) to interact with
86/// the main store. This provides namespace isolation and ensures that different workers
87/// can operate without conflicts. Typically, a worker implementation will:
88///
89/// 1. Receive a reference to the main store in its `build` method
90/// 2. Create a `WorkerStore` instance with the worker's name as prefix
91/// 3. Use the `WorkerStore` to update asset information
92///
93/// # Examples
94///
95/// Implementing a simple HTTP polling worker:
96///
97/// ```rust
98/// use std::time::Duration;
99/// use async_trait::async_trait;
100/// use bothan_lib::worker::{AssetWorker, error::AssetWorkerError};
101/// use bothan_lib::store::{Store, WorkerStore};
102/// use bothan_lib::types::AssetInfo;
103/// use rust_decimal::Decimal;
104///
105/// // Worker configuration
106/// #[derive(Clone)]
107/// struct HttpPollerOpts {
108///     poll_interval: Duration,
109///     endpoint: String,
110/// }
111///
112/// // Worker implementation
113/// struct HttpPoller {
114///     opts: HttpPollerOpts,
115///     ids: Vec<String>,
116/// }
117///
118/// #[async_trait]
119/// impl AssetWorker for HttpPoller {
120///     type Opts = HttpPollerOpts;
121///
122///     fn name(&self) -> &'static str {
123///         "http_poller"
124///     }
125///
126///     async fn build<S: Store + 'static>(
127///         opts: Self::Opts,
128///         store: &S,
129///         ids: Vec<String>,
130///     ) -> Result<Self, AssetWorkerError> {
131///         // Create a worker-specific store with the worker's name as prefix
132///         let worker_store = WorkerStore::new(store, "http_poller");
133///
134///         // In a real implementation, you would also have methods for:
135///         // - Starting the worker (data collection loop)
136///         // - Handling errors and retries
137///         // - Shutting down gracefully
138///         
139///         Ok(HttpPoller {
140///             opts,
141///             ids,
142///         })
143///     }
144/// }
145/// ```
146#[async_trait::async_trait]
147pub trait AssetWorker: Send + Sync + Sized {
148    /// Configuration options for the worker.
149    ///
150    /// This associated type defines the configuration parameters needed to
151    /// construct an instance of the worker.
152    type Opts;
153
154    /// Returns the name of the worker.
155    ///
156    /// The name serves as a unique identifier for the worker. When used in conjunction
157    /// with Bothan's registry, the worker's name should match the source name specified
158    /// in the registry.
159    fn name(&self) -> &'static str;
160
161    /// Builds a new worker instance with the specified options and query IDs.
162    ///
163    /// This method is responsible for creating and initializing a new worker
164    /// instance with the provided configuration and query IDs to monitor.
165    ///
166    /// # Errors
167    ///
168    /// Returns an `AssetWorkerError` if the worker cannot be built with the given
169    /// parameters, such as when the configuration is invalid or required resources
170    /// are unavailable.
171    async fn build<S: Store + 'static>(
172        opts: Self::Opts,
173        store: &S,
174        ids: Vec<String>,
175    ) -> Result<Self, AssetWorkerError>;
176}