binary_options_tools_core_pre/
traits.rs

1use async_trait::async_trait;
2use kanal::{AsyncReceiver, AsyncSender};
3use std::fmt::Debug;
4use std::sync::Arc;
5use tokio_tungstenite::tungstenite::Message;
6
7use crate::error::CoreResult;
8
9/// The contract for the application's shared state.
10#[async_trait]
11pub trait AppState: Send + Sync + 'static {
12    /// Clears any temporary data from the state, called on a manual disconnect.
13    async fn clear_temporal_data(&self);
14}
15
16#[async_trait]
17impl AppState for () {
18    async fn clear_temporal_data(&self) {
19        // Default implementation does nothing.
20    }
21}
22
23/// The contract for a self-contained, concurrent API module.
24/// Generic over the `AppState` for type-safe access to shared data.
25#[async_trait]
26pub trait ApiModule<S: AppState>: Send + 'static {
27    /// The specific command type this module accepts.
28    type Command: Debug + Send;
29    /// This specific CommandResponse type this module produces.
30    type CommandResponse: Debug + Send;
31    /// The handle that users will interact with. It must be clonable.
32    type Handle: Clone + Send + Sync + 'static;
33
34    /// Creates a new instance of the module.
35    fn new(
36        shared_state: Arc<S>,
37        command_receiver: AsyncReceiver<Self::Command>,
38        command_responder: AsyncSender<Self::CommandResponse>,
39        message_receiver: AsyncReceiver<Arc<Message>>,
40        to_ws_sender: AsyncSender<Message>,
41    ) -> Self
42    where
43        Self: Sized;
44
45    /// Creates a new handle for this module.
46    /// This is used to send commands to the module.
47    ///
48    /// # Arguments
49    /// * `sender`: The sender channel for commands.
50    /// * `receiver`: The receiver channel for command responses.
51    fn create_handle(
52        sender: AsyncSender<Self::Command>,
53        receiver: AsyncReceiver<Self::CommandResponse>,
54    ) -> Self::Handle;
55
56    fn new_combined(
57        shared_state: Arc<S>,
58        command_receiver: AsyncReceiver<Self::Command>,
59        command_responder: AsyncSender<Self::Command>,
60        command_response_receiver: AsyncReceiver<Self::CommandResponse>,
61        command_response_responder: AsyncSender<Self::CommandResponse>,
62        message_receiver: AsyncReceiver<Arc<Message>>,
63        to_ws_sender: AsyncSender<Message>,
64    ) -> (Self, Self::Handle)
65    where
66        Self: Sized,
67    {
68        let module = Self::new(
69            shared_state,
70            command_receiver,
71            command_response_responder,
72            message_receiver,
73            to_ws_sender,
74        );
75        let handle = Self::create_handle(command_responder, command_response_receiver);
76        (module, handle)
77    }
78
79    /// The main run loop for the module's background task.
80    async fn run(&mut self) -> CoreResult<()>;
81
82    /// An optional callback that can be executed when a reconnection event occurs.
83    /// This function is useful for modules that need to perform specific actions
84    /// when a reconnection happens, such as reinitializing state or resending messages.
85    /// It allows for custom behavior to be defined that can be executed in the context of the
86    /// module, providing flexibility and extensibility to the module's functionality.
87    fn callback(&self) -> CoreResult<Option<Box<dyn ReconnectCallback<S>>>> {
88        // Default implementation does nothing.
89        // This is useful for modules that do not require a callback.
90        Ok(None)
91    }
92
93    /// Route only messages for which this returns true.
94    /// This function is used to determine whether a message should be processed by this module.
95    /// It allows for flexible and reusable rules that can be applied to different modules.
96    /// The main difference between this and the `LightweightModule` rule is that
97    /// this rule also takes the shared state as an argument, allowing for more complex
98    /// routing logic that can depend on the current state of the application.
99    fn rule(state: Arc<S>) -> Box<dyn Rule + Send + Sync>;
100}
101
102/// A self‐contained module that runs independently,
103/// owns its recv/sender channels and shared state,
104/// and processes incoming WS messages according to its routing rule.
105/// It's main difference from `ApiModule` is that it does not
106/// require a command-response mechanism and is not intended to be used
107/// as a part of the API, but rather as a lightweight module that can
108/// process messages in a more flexible way.
109/// It is useful for modules that need to handle messages without the overhead of a full API module
110/// and can be used for tasks like logging, monitoring, or simple message transformations.
111/// It is designed to be lightweight and efficient, allowing for quick processing of messages
112/// without the need for a full command-response cycle.
113/// It is also useful for modules that need to handle messages in a more flexible way,
114/// such as forwarding messages to other parts of the system or performing simple transformations.
115/// It is not intended to be used as a part of the API, but rather as a
116/// lightweight module that can process messages in a more flexible way.
117///
118/// The main difference from the `LightweightHandler` type is that this trait is intended for
119/// modules that need to manage their own state and processing logic and being run in a dedicated task.,
120/// allowing easy automation of things like sending periodic messages to a websocket connection to keep it alive.
121#[async_trait]
122pub trait LightweightModule<S: AppState>: Send + 'static {
123    /// Construct the module with:
124    /// - shared app state
125    /// - a sender for outgoing WS messages
126    /// - a receiver for incoming WS messages
127    fn new(
128        state: Arc<S>,
129        ws_sender: AsyncSender<Message>,
130        ws_receiver: AsyncReceiver<Arc<Message>>,
131    ) -> Self
132    where
133        Self: Sized;
134
135    /// The module's asynchronous run loop.
136    async fn run(&mut self) -> CoreResult<()>;
137
138    /// Route only messages for which this returns true.
139    fn rule() -> Box<dyn Rule + Send + Sync>;
140}
141
142/// Data returned by the rule function of a module.
143/// This trait is used to define the rules that determine whether a message should be processed by a module.
144/// It allows for flexible and reusable rules that can be applied to different modules.
145/// The rules can be implemented as standalone functions or as methods on the module itself.
146/// The rules should be lightweight and efficient, as they will be called for every incoming message.
147/// The rules should not perform any blocking operations and should be designed to be as efficient as possible
148/// to avoid slowing down the message processing pipeline.
149/// The rules can be used to filter messages, transform them, or perform any other necessary operations
150pub trait Rule {
151    /// Validate wherever the messsage follows the rule and needs to be processed by this module.
152    fn call(&self, msg: &Message) -> bool;
153
154    /// Resets the rule to its initial state.
155    /// This is useful for rules that maintain state and need to be reset
156    /// when the module is reset or reinitialized.
157    /// Implementations should ensure that the rule is in a clean state after this call.
158    /// # Note
159    /// This method is not required to be asynchronous, as it is expected to be a lightweight
160    /// operation that does not involve any I/O or long-running tasks.
161    /// It should be implemented in a way that allows the rule to be reused without
162    /// needing to recreate it, thus improving performance and reducing overhead.
163    fn reset(&self);
164}
165
166/// A trait for callback functions that can be executed within the context of a module.
167/// This trait is designed to allow modules to define custom behavior that can be executed
168/// when a reconnection event occurs.
169#[async_trait]
170pub trait ReconnectCallback<S: AppState>: Send + Sync {
171    /// The asynchronous function that will be called when a reconnection event occurs.
172    /// This function receives the shared state and a sender for outgoing WebSocket messages.
173    /// It should return a `CoreResult<()>` indicating the success or failure of the operation.
174    /// /// # Arguments
175    /// * `state`: The shared application state that the callback can use.
176    /// * `ws_sender`: The sender for outgoing WebSocket messages, allowing the callback to
177    ///   send messages to the WebSocket connection.
178    /// # Returns
179    /// A `CoreResult<()>` indicating the success or failure of the operation.
180    /// # Note
181    /// This function is expected to be asynchronous, allowing it to perform I/O operations
182    /// or other tasks that may take time without blocking the event loop.
183    /// Implementations should ensure that they handle any potential errors gracefully
184    /// and return appropriate results.
185    /// It is also important to ensure that the callback does not block the event loop,
186    /// as this could lead to performance issues in the application.
187    /// Implementations should be designed to be efficient and non-blocking,
188    /// allowing the application to continue processing other events while the callback is executed.
189    /// This trait is useful for defining custom behavior that can be executed when a reconnection event
190    /// occurs, allowing modules to handle reconnections in a flexible and reusable way.    
191    async fn call(&self, state: Arc<S>, ws_sender: &AsyncSender<Message>) -> CoreResult<()>;
192}
193
194impl<F> Rule for F
195where
196    F: Fn(&Message) -> bool + Send + Sync + 'static,
197{
198    fn call(&self, msg: &Message) -> bool {
199        self(msg)
200    }
201
202    fn reset(&self) {
203        // Default implementation does nothing.
204        // This is useful for stateless rules.
205    }
206}
207
208#[async_trait]
209impl<S: AppState> ReconnectCallback<S> for () {
210    async fn call(&self, _state: Arc<S>, _ws_sender: &AsyncSender<Message>) -> CoreResult<()> {
211        Ok(())
212    }
213}