Skip to main content

acts_package_state/
package.rs

1use crate::config::StateConfig;
2use acts::{
3    ActError, ActPackage, ActPackageCatalog, ActPackageDefinition, ActResource, ActRunAs, Result,
4    Vars,
5};
6use serde::{Deserialize, Serialize};
7use serde_json::json;
8
9const CONFIG_NAME: &str = "state";
10
11#[derive(Debug, Clone)]
12pub struct StatePackage {
13    client: redis::Client,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct StatePackageParams {
18    op: String,
19    params: Vars,
20}
21
22impl ActPackage for StatePackage {
23    fn definition() -> acts::ActPackageDefinition {
24        ActPackageDefinition {
25            id: "acts.app.state",
26            name: "State",
27            desc: "get or set state to redis",
28            version: "0.1.0",
29            icon: "icon-app-state",
30            doc: "",
31            schema: json!({
32                "type": "object",
33                "properties": {
34                    "op": { "type": "string", "enum": ["GET", "SET" ] },
35                    "key": { "type": "string" },
36                    "value": { "type": ["number", "string", "boolean", "array", "object"] },
37                },
38                "required": ["op", "key"],
39            }),
40            options: Some(json!({
41                "ui:order": ["op", "key", "value"],
42                "op": {
43                    "ui:widget": "select",
44                    "ui:options": {
45                        "label": false,
46                        "placeholder": "Select an operation"
47                    }
48                },
49                "key": {
50                    "ui:widget": "text",
51                    "ui:options": {
52                        "label": false,
53                        "placeholder": "Enter the key"
54                    }
55                },
56                "value": {
57                    "ui:widget": "textarea",
58                    "ui:options": {
59                        "label": false,
60                        "placeholder": "Enter the value"
61                    }
62                }
63            })),
64            run_as: ActRunAs::Irq,
65            resources: vec![
66                ActResource {
67                    name: "Get state store".to_string(),
68                    desc: "get a state from the state store".to_string(),
69                    value: json!({ "op": "GET"}),
70                },
71                ActResource {
72                    name: "Set state store".to_string(),
73                    desc: "set a state from the state store".to_string(),
74                    value: json!({ "op": "SET"}),
75                },
76            ],
77            catalog: ActPackageCatalog::App,
78        }
79    }
80
81    fn new(config: &acts::Config) -> Result<Self>
82    where
83        Self: Sized,
84    {
85        if !config.has(CONFIG_NAME) {
86            return Err(acts::ActError::Config(
87                "missing 'state' section in config file".to_string(),
88            ));
89        }
90        let config = config
91            .get::<StateConfig>(CONFIG_NAME)
92            .map_err(|err| acts::ActError::Config(format!("get state config error: {err}")))?;
93
94        let mut client = redis::Client::open(config.database_uri.as_str())
95            .map_err(|err| acts::ActError::Config(format!("create redis client error: {err}")))?;
96
97        redis::cmd("PING")
98            .exec(&mut client)
99            .map_err(|err| acts::ActError::Config(format!("ping redis error: {err}")))?;
100        Ok(Self { client })
101    }
102
103    fn execute(&self, ctx: &acts::Context, params: &serde_json::Value) -> Result<Option<Vars>> {
104        let mut conn = self
105            .client
106            .get_connection()
107            .map_err(|err| ActError::Package(format!("error happend to get connection: {err}")))?;
108
109        let pid = ctx.task().pid.to_string();
110        let params = serde_json::from_value::<StatePackageParams>(params.clone()).map_err(|e| {
111            ActError::Package(format!(
112                "invalid ActPackage({}) params: {}",
113                Self::definition().id,
114                e
115            ))
116        })?;
117        match params.op.as_str() {
118            "GET" => {
119                let key = params
120                    .params
121                    .get::<String>("key")
122                    .ok_or(ActError::Package("missing 'key' in params".to_string()))?
123                    .to_string();
124
125                let ret = redis::cmd("GET")
126                    .arg(format!("{pid}:{key}"))
127                    .query::<String>(&mut conn)
128                    .map_err(|err| {
129                        ActError::Package(format!("error happend to set value: {err}"))
130                    })?;
131
132                let mut vars = Vars::new();
133                vars.insert(
134                    key,
135                    serde_json::from_str(&ret).map_err(|err| {
136                        ActError::Package(format!("error happend to parse value: {err}"))
137                    })?,
138                );
139
140                Ok(Some(vars))
141            }
142            "SET" => {
143                let key = params
144                    .params
145                    .get::<String>("key")
146                    .ok_or(ActError::Package("missing 'key' in params".to_string()))?
147                    .to_string();
148
149                let value: serde_json::Value = params
150                    .params
151                    .get("value")
152                    .ok_or(ActError::Package("missing 'value' in params".to_string()))?;
153
154                let v = serde_json::to_string(&value).map_err(|err| {
155                    ActError::Package(format!("error happend to parse value: {err}"))
156                })?;
157
158                redis::cmd("SET")
159                    .arg(format!("{pid}:{key}"))
160                    .arg(v.as_str())
161                    .query::<String>(&mut conn)
162                    .map_err(|err| {
163                        ActError::Package(format!("error happend to set value: {err}"))
164                    })?;
165                Ok(None)
166            }
167            _ => Err(ActError::Package(format!(
168                "invalid operation: {}",
169                params.op
170            ))),
171        }
172    }
173}