acts_package_state/
package.rs1use 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}