Skip to main content

mocra_core/common/interface/
module.rs

1use crate::common::model::login_info::LoginInfo;
2use crate::common::model::module_dag::ModuleDagDefinition;
3use crate::common::model::{Cookies, CronConfig, Headers, ModuleConfig, Request, Response};
4// use crate::common::parser::ParserTrait;
5use crate::common::model::message::TaskOutputEvent;
6use crate::errors::Result;
7use async_trait::async_trait;
8use futures::Stream;
9use serde_json::Map;
10use std::pin::Pin;
11use std::sync::Arc;
12
13pub type SyncBoxStream<'a, T> = Pin<Box<dyn Stream<Item = T> + Send + Sync + 'a>>;
14
15#[async_trait]
16pub trait ModuleTrait: Send + Sync {
17    fn should_login(&self) -> bool {
18        true
19    }
20    fn name(&self) -> String;
21    fn version(&self) -> i32;
22    async fn headers(&self) -> Headers {
23        Headers::default()
24    }
25    async fn cookies(&self) -> Cookies {
26        Cookies::default()
27    }
28
29    fn default_arc() -> Arc<dyn ModuleTrait>
30    where
31        Self: Sized;
32    /// Optional custom DAG definition built from ModuleNodeTrait nodes.
33    ///
34    /// - `Some(definition)`: use custom DAG path.
35    /// - `None`: fallback to legacy `add_step` linear-compat DAG.
36    async fn dag_definition(&self) -> Option<ModuleDagDefinition> {
37        None
38    }
39    async fn add_step(&self) -> Vec<Arc<dyn ModuleNodeTrait>> {
40        vec![]
41    }
42    async fn pre_process(&self, _config: Option<Arc<ModuleConfig>>) -> Result<()> {
43        Ok(())
44    }
45    // Called after `ModuleDagProcessor` finishes all nodes, for finalization logic.
46    // Responses may not yet pass through `DataMiddleware`, so do not depend on final processed output.
47    async fn post_process(&self, _config: Option<Arc<ModuleConfig>>) -> Result<()> {
48        Ok(())
49    }
50    /// Returns cron schedule config for this module.
51    /// Defaults to `None` (scheduled startup disabled).
52    fn cron(&self) -> Option<CronConfig> {
53        None
54    }
55}
56
57/// `ModuleNodeTrait` defines per-node request generation and response parsing.
58/// Data across nodes is currently propagated via metadata:
59/// `Request -> Response -> TaskParserEvent -> Request`.
60/// Ideally node structs are immutable during execution; when mutable progression state
61/// (e.g. pagination) is needed, it should be carried in metadata.
62#[async_trait]
63pub trait ModuleNodeTrait: Send + Sync {
64    async fn generate(
65        &self,
66        _config: Arc<ModuleConfig>,
67        _params: Map<String, serde_json::Value>,
68        _login_info: Option<LoginInfo>,
69    ) -> Result<SyncBoxStream<'static, Request>>;
70    async fn parser(
71        &self,
72        response: Response,
73        _config: Option<Arc<ModuleConfig>>,
74    ) -> Result<TaskOutputEvent>;
75    fn retryable(&self) -> bool {
76        true
77    }
78    /// Returns a stable, unique string key for this node type within the DAG.
79    ///
80    /// When non-empty, `ModuleDagNodeDef` uses this value directly as the node ID
81    /// instead of a randomly generated UUID. This ensures node IDs remain consistent
82    /// across DAG rebuilds (e.g. after error-task retries cause the factory to
83    /// reconstruct the module instance), so error retry routing works correctly.
84    ///
85    /// Override this and return a short constant string that is unique among all
86    /// nodes in the same module's DAG. Default is `""` (random UUID, existing
87    /// behavior preserved for backward compatibility).
88    fn stable_node_key(&self) -> &'static str {
89        ""
90    }
91}
92
93pub trait ToSyncBoxStream<T> {
94    fn to_stream(self) -> SyncBoxStream<'static, T>;
95    fn into_stream_ok(self) -> Result<SyncBoxStream<'static, T>>;
96}
97
98impl<T> ToSyncBoxStream<T> for Vec<T>
99where
100    T: Send + Sync + 'static,
101{
102    fn to_stream(self) -> SyncBoxStream<'static, T> {
103        Box::pin(futures::stream::iter(self))
104    }
105
106    fn into_stream_ok(self) -> Result<SyncBoxStream<'static, T>> {
107        Ok(Box::pin(futures::stream::iter(self)))
108    }
109}