Skip to main content

acts/package/
mod.rs

1pub mod core;
2pub mod transform;
3
4#[cfg(test)]
5mod tests;
6
7use crate::{
8    Config, Engine, Result, Vars, data,
9    scheduler::{Context, Runtime},
10    store::DbCollectionIden,
11};
12use dashmap::DashMap;
13use serde::{Deserialize, Serialize};
14use std::{fmt::Debug, sync::Arc};
15use tracing::debug;
16
17#[cfg(test)]
18pub use core::RunningMode;
19
20#[derive(Debug, Clone)]
21pub struct Package {
22    packages: Arc<DashMap<String, ActPackageRegister>>,
23}
24
25#[async_trait::async_trait]
26
27pub trait ActPackage: Send + Sync {
28    /// create package instance with config
29    fn new(config: &Config) -> Result<Self>
30    where
31        Self: Sized;
32    /// get package meta definition
33    fn definition() -> ActPackageDefinition
34    where
35        Self: Sized;
36    /// executing with task context
37    async fn execute(&self, _ctx: &Context, _params: &serde_json::Value) -> Result<Option<Vars>> {
38        Ok(None)
39    }
40    /// start with non-context, such as workflow event
41    async fn start(
42        &self,
43        _rt: &Arc<Runtime>,
44        _params: &serde_json::Value,
45        _options: &Vars,
46    ) -> Result<Option<Vars>> {
47        Ok(None)
48    }
49}
50
51#[derive(
52    Serialize,
53    Deserialize,
54    Debug,
55    Clone,
56    Copy,
57    Default,
58    PartialEq,
59    strum::AsRefStr,
60    strum::EnumString,
61)]
62#[serde(rename_all = "snake_case")]
63#[strum(serialize_all = "snake_case")]
64pub enum ActRunAs {
65    /// only used internally
66    Func,
67    /// interrupt request, need to response
68    #[default]
69    Irq,
70    /// message without response
71    Msg,
72}
73
74#[derive(
75    Serialize,
76    Deserialize,
77    Debug,
78    Clone,
79    Copy,
80    Default,
81    PartialEq,
82    strum::AsRefStr,
83    strum::EnumString,
84)]
85#[serde(rename_all = "snake_case")]
86#[strum(serialize_all = "snake_case")]
87pub enum ActPackageCatalog {
88    /// acts core packages
89    Core,
90
91    /// workflow event
92    Event,
93
94    /// workflow trace package
95    Output,
96
97    /// data transform
98    Transform,
99
100    /// form submition
101    Form,
102
103    /// AI related for LLMs
104    Ai,
105
106    /// the other applications to integrate into acts
107    /// such as Store, State, Observability, Pubsub
108    #[default]
109    App,
110}
111
112#[derive(Debug, Clone, Deserialize, Serialize)]
113pub struct ActPackageDefinition {
114    /// package id, used to identify the package
115    pub id: &'static str,
116
117    /// package simple name
118    pub name: &'static str,
119
120    /// package description
121    pub desc: &'static str,
122
123    /// icon name to display in the editor ui
124    pub icon: &'static str,
125
126    /// releated doc url to show the help
127    pub doc: &'static str,
128
129    /// package version
130    pub version: &'static str,
131
132    /// json schema for package params
133    pub schema: serde_json::Value,
134
135    /// extra options
136    #[serde(default)]
137    pub options: Option<serde_json::Value>,
138
139    /// package run as Irq, Msg or Func
140    /// Func is only used internally
141    pub run_as: ActRunAs,
142
143    /// package resources to the orgnize multiple resources
144    /// it is used for the editor ui to search and select the resources
145    /// each resource value can fill the special value into the UI
146    pub resources: Vec<ActResource>,
147
148    /// package catalog
149    pub catalog: ActPackageCatalog,
150}
151
152#[derive(Debug, Clone, Deserialize, Serialize)]
153pub struct ActResource {
154    pub name: String,
155    pub desc: String,
156    pub value: serde_json::Value,
157}
158
159#[derive(Debug, Clone)]
160pub struct ActPackageRegister {
161    pub meta: fn() -> ActPackageDefinition,
162    pub create: fn(config: &Config) -> Result<Arc<dyn ActPackage>>,
163}
164
165impl ActPackageRegister {
166    pub(crate) const fn new<T>() -> Self
167    where
168        T: ActPackage + 'static,
169    {
170        Self {
171            meta: T::definition,
172            create: (|config: &Config| {
173                // let meta = T::definition();
174                // jsonschema::validate(&meta.schema, params).map_err(|err| {
175                //     ActError::Package(format!(
176                //         "package({}) schema validation error: {}",
177                //         meta.id, err
178                //     ))
179                // })?;
180
181                let ret = T::new(config)?;
182                Ok(Arc::new(ret) as Arc<dyn ActPackage>)
183            }),
184        }
185    }
186}
187
188impl Default for Package {
189    fn default() -> Self {
190        Self::new()
191    }
192}
193
194impl Package {
195    pub fn new() -> Self {
196        Self {
197            packages: Arc::new(DashMap::new()),
198        }
199    }
200
201    pub fn register(&self, id: &str, register: &ActPackageRegister) {
202        self.packages.insert(id.to_string(), register.clone());
203    }
204
205    pub fn get(&self, id: &str) -> Option<ActPackageRegister> {
206        self.packages.get(id).map(|v| v.clone())
207    }
208}
209
210impl ActPackageDefinition {
211    pub fn into_data(&self) -> Result<data::Package> {
212        let pack = self.clone();
213        Ok(data::Package {
214            id: pack.id.to_string(),
215            name: pack.name.to_string(),
216            desc: pack.desc.to_string(),
217            icon: pack.icon.to_string(),
218            doc: pack.doc.to_string(),
219            version: pack.version.to_string(),
220            schema: pack.schema.to_string(),
221            options: pack.options.map(|v| v.to_string()),
222            run_as: pack.run_as,
223            resources: serde_json::to_string(&pack.resources)
224                .expect("cannot convert ActPackageMeta.resources to json"),
225            catalog: pack.catalog,
226            create_time: 0,
227            update_time: 0,
228            timestamp: 0,
229            built_in: false,
230            v: data::Package::version(),
231        })
232    }
233}
234
235inventory::collect!(ActPackageRegister);
236
237pub async fn init(engine: &Engine) -> Result<()> {
238    for register in inventory::iter::<ActPackageRegister> {
239        let meta = (register.meta)();
240        debug!("package: {}", meta.name);
241
242        let mut pack = meta.into_data()?;
243        pack.built_in = true;
244        engine.executor().pack().publish(&pack).await?;
245        engine.runtime().package().register(meta.id, register);
246    }
247    Ok(())
248}