1use std::collections::HashMap;
3use serde::{Serialize, Deserialize};
4use serde_json::Value;
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct ActionParameter {
8 pub name: String,
9 pub description: String,
10 pub required: bool,
11 pub param_type: ParamType,
12 pub default_value: Option<Value>,
13}
14
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub enum ParamType {
17 String,
18 Number,
19 Boolean,
20 Object,
21 Array,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct ActionDefinition {
26 pub name: String,
27 pub description: String,
28 pub parameters: Vec<ActionParameter>,
29}
30
31pub type ActionResult = Result<Value, String>;
32
33pub trait CpiExtension: Send + Sync {
35 fn name(&self) -> &str;
37
38 fn provider_type(&self) -> &str;
40
41 fn list_actions(&self) -> Vec<String>;
43
44 fn get_action_definition(&self, action: &str) -> Option<ActionDefinition>;
46
47 fn execute_action(&self, action: &str, params: &HashMap<String, Value>) -> ActionResult;
49
50 fn default_settings(&self) -> HashMap<String, Value> {
52 HashMap::new()
53 }
54
55 fn test_install(&self) -> ActionResult {
57 Ok(serde_json::json!({"status": "ok"}))
59 }
60
61 fn version(&self) -> String {
62 "NONE".to_string()
64 }
65}
66
67pub type GetExtensionFn = unsafe extern "C" fn() -> *mut dyn CpiExtension;
69
70pub use lib_cpi_macros::action;
73
74#[macro_export]
76macro_rules! register_extension {
77 ($ext_type:ty) => {
78 #[no_mangle]
79 pub unsafe extern "C" fn get_extension() -> *mut dyn $crate::CpiExtension {
80 let extension = Box::new(<$ext_type>::new());
82 Box::into_raw(extension)
84 }
85 };
86}
87
88pub mod validation {
90 use super::*;
91
92 pub fn extract_string(params: &HashMap<String, Value>, name: &str) -> Result<String, String> {
93 match params.get(name) {
94 Some(Value::String(s)) => Ok(s.clone()),
95 Some(_) => Err(format!("Parameter '{}' must be a string", name)),
96 None => Err(format!("Required parameter '{}' not provided", name)),
97 }
98 }
99
100 pub fn extract_string_opt(params: &HashMap<String, Value>, name: &str) -> Result<Option<String>, String> {
101 match params.get(name) {
102 Some(Value::String(s)) => Ok(Some(s.clone())),
103 Some(_) => Err(format!("Parameter '{}' must be a string", name)),
104 None => Ok(None),
105 }
106 }
107
108 pub fn extract_int(params: &HashMap<String, Value>, name: &str) -> Result<i64, String> {
109 match params.get(name) {
110 Some(Value::Number(n)) if n.is_i64() => Ok(n.as_i64().unwrap()),
111 Some(_) => Err(format!("Parameter '{}' must be an integer", name)),
112 None => Err(format!("Required parameter '{}' not provided", name)),
113 }
114 }
115
116 pub fn extract_int_opt(params: &HashMap<String, Value>, name: &str) -> Result<Option<i64>, String> {
117 match params.get(name) {
118 Some(Value::Number(n)) if n.is_i64() => Ok(Some(n.as_i64().unwrap())),
119 Some(_) => Err(format!("Parameter '{}' must be an integer", name)),
120 None => Ok(None),
121 }
122 }
123
124 pub fn extract_float(params: &HashMap<String, Value>, name: &str) -> Result<f64, String> {
125 match params.get(name) {
126 Some(Value::Number(n)) => Ok(n.as_f64().unwrap()),
127 Some(_) => Err(format!("Parameter '{}' must be a number", name)),
128 None => Err(format!("Required parameter '{}' not provided", name)),
129 }
130 }
131
132 pub fn extract_bool(params: &HashMap<String, Value>, name: &str) -> Result<bool, String> {
133 match params.get(name) {
134 Some(Value::Bool(b)) => Ok(*b),
135 Some(_) => Err(format!("Parameter '{}' must be a boolean", name)),
136 None => Err(format!("Required parameter '{}' not provided", name)),
137 }
138 }
139
140 pub fn extract_json(params: &HashMap<String, Value>, name: &str) -> Result<Value, String> {
141 match params.get(name) {
142 Some(v) => Ok(v.clone()),
143 None => Err(format!("Required parameter '{}' not provided", name)),
144 }
145 }
146
147 pub fn validate_params(
148 params: &HashMap<String, Value>,
149 required: &[&str]
150 ) -> Result<(), String> {
151 for ¶m in required {
152 if !params.contains_key(param) {
153 return Err(format!("Required parameter '{}' not provided", param));
154 }
155 }
156 Ok(())
157 }
158}
159
160#[macro_export]
162macro_rules! param {
163 ($name:expr, $desc:expr, $type:expr, required) => {
164 $crate::ActionParameter {
165 name: $name.to_string(),
166 description: $desc.to_string(),
167 required: true,
168 param_type: $type,
169 default_value: None,
170 }
171 };
172 ($name:expr, $desc:expr, $type:expr, optional, $default:expr) => {
173 $crate::ActionParameter {
174 name: $name.to_string(),
175 description: $desc.to_string(),
176 required: false,
177 param_type: $type,
178 default_value: Some($default),
179 }
180 };
181 ($name:expr, $desc:expr, $type:expr, optional) => {
182 $crate::ActionParameter {
183 name: $name.to_string(),
184 description: $desc.to_string(),
185 required: false,
186 param_type: $type,
187 default_value: None,
188 }
189 };
190}
191
192pub mod response {
194 use serde_json::{json, Value};
195
196 pub fn success(data: Option<Value>) -> Value {
197 match data {
198 Some(value) => json!({
199 "success": true,
200 "data": value,
201 }),
202 None => json!({
203 "success": true,
204 }),
205 }
206 }
207
208 pub fn bool_result(result: bool) -> Value {
209 json!({
210 "success": true,
211 "result": result
212 })
213 }
214
215 pub fn error(message: impl AsRef<str>) -> Value {
216 json!({
217 "success": false,
218 "error": message.as_ref(),
219 })
220 }
221}