Skip to main content

acts_next/package/
mod.rs

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