apcore_toolkit/
serializers.rs1use serde_json::{json, Value};
7use tracing::warn;
8
9use apcore::module::ModuleAnnotations;
10
11use crate::types::ScannedModule;
12
13pub fn annotations_to_dict(annotations: Option<&ModuleAnnotations>) -> Value {
17 match annotations {
18 Some(ann) => serde_json::to_value(ann).unwrap_or_else(|e| {
19 warn!("Failed to serialize ModuleAnnotations: {e}");
20 Value::Null
21 }),
22 None => Value::Null,
23 }
24}
25
26pub fn module_to_dict(module: &ScannedModule) -> Value {
36 let examples = serde_json::to_value(&module.examples).unwrap_or_else(|e| {
37 warn!(
38 module_id = %module.module_id,
39 "Failed to serialize examples: {e}"
40 );
41 json!([])
42 });
43
44 json!({
45 "module_id": module.module_id,
46 "description": module.description,
47 "documentation": module.documentation,
48 "tags": module.tags,
49 "version": module.version,
50 "target": module.target,
51 "annotations": annotations_to_dict(module.annotations.as_ref()),
52 "suggested_alias": module.suggested_alias,
53 "examples": examples,
54 "metadata": module.metadata,
55 "input_schema": module.input_schema,
56 "output_schema": module.output_schema,
57 "display": module.display,
58 "warnings": module.warnings,
59 })
60}
61
62pub fn modules_to_dicts(modules: &[ScannedModule]) -> Vec<Value> {
64 modules.iter().map(module_to_dict).collect()
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use serde_json::json;
71
72 fn sample_module() -> ScannedModule {
73 ScannedModule::new(
74 "users.get".into(),
75 "Get user".into(),
76 json!({"type": "object"}),
77 json!({"type": "object"}),
78 vec!["users".into()],
79 "app:get_user".into(),
80 )
81 }
82
83 #[test]
84 fn test_annotations_to_dict_none() {
85 assert_eq!(annotations_to_dict(None), Value::Null);
86 }
87
88 #[test]
89 fn test_annotations_to_dict_some() {
90 let ann = ModuleAnnotations {
91 readonly: true,
92 ..Default::default()
93 };
94 let val = annotations_to_dict(Some(&ann));
95 assert_eq!(val["readonly"], true);
96 }
97
98 #[test]
99 fn test_module_to_dict() {
100 let m = sample_module();
101 let val = module_to_dict(&m);
102 assert_eq!(val["module_id"], "users.get");
103 assert_eq!(val["description"], "Get user");
104 assert_eq!(val["version"], "1.0.0");
105 assert_eq!(val["target"], "app:get_user");
106 assert!(val["tags"].is_array());
107 }
108
109 #[test]
110 fn test_modules_to_dicts() {
111 let modules = vec![sample_module(), sample_module()];
112 let values = modules_to_dicts(&modules);
113 assert_eq!(values.len(), 2);
114 }
115
116 #[test]
117 fn test_module_to_dict_with_annotations() {
118 let mut m = sample_module();
119 m.annotations = Some(ModuleAnnotations {
120 destructive: true,
121 ..Default::default()
122 });
123 let val = module_to_dict(&m);
124 assert_eq!(val["annotations"]["destructive"], true);
125 }
126
127 #[test]
128 fn test_module_to_dict_all_keys() {
129 let val = module_to_dict(&sample_module());
130 let obj = val.as_object().unwrap();
131 let expected_keys: std::collections::HashSet<&str> = [
132 "module_id",
133 "description",
134 "documentation",
135 "tags",
136 "version",
137 "target",
138 "annotations",
139 "suggested_alias",
140 "examples",
141 "metadata",
142 "input_schema",
143 "output_schema",
144 "display",
145 "warnings",
146 ]
147 .into_iter()
148 .collect();
149
150 let actual_keys: std::collections::HashSet<&str> = obj.keys().map(|k| k.as_str()).collect();
151
152 assert_eq!(actual_keys, expected_keys);
153 }
154
155 #[test]
156 fn test_module_to_dict_includes_suggested_alias() {
157 let mut m = sample_module();
158 m.suggested_alias = Some("users.get_alias".into());
159 let val = module_to_dict(&m);
160 assert_eq!(val["suggested_alias"], "users.get_alias");
161 }
162
163 #[test]
164 fn test_module_to_dict_suggested_alias_null_when_absent() {
165 let val = module_to_dict(&sample_module());
166 assert!(val.as_object().unwrap().contains_key("suggested_alias"));
167 assert_eq!(val["suggested_alias"], Value::Null);
168 }
169
170 #[test]
171 fn test_module_to_dict_warnings_empty_default() {
172 let val = module_to_dict(&sample_module());
173 let warnings = val["warnings"].as_array().unwrap();
174 assert!(warnings.is_empty());
175 }
176
177 #[test]
178 fn test_module_to_dict_with_documentation() {
179 let mut m = sample_module();
180 m.documentation = Some("Detailed documentation".into());
181 let val = module_to_dict(&m);
182 assert_eq!(val["documentation"], "Detailed documentation");
183 }
184
185 #[test]
186 fn test_module_to_dict_examples_empty_default() {
187 let val = module_to_dict(&sample_module());
188 let examples = val["examples"].as_array().unwrap();
189 assert!(examples.is_empty());
190 }
191
192 #[test]
193 fn test_modules_to_dicts_empty() {
194 let values = modules_to_dicts(&[]);
195 assert!(values.is_empty());
196 }
197}