1use anyhow::{Context, Result};
10use serde::de::DeserializeOwned;
11use serde_json::Value;
12use std::collections::HashMap;
13
14pub mod config;
15pub mod models;
16mod parser;
17pub mod schemas;
18
19pub use config::*;
20pub use models::*;
21pub use parser::{parse_ccl, CclValue};
22pub use schemas::*;
23
24pub fn parse_to_hashmap(ccl_content: &str) -> Result<HashMap<String, Value>> {
50 let model = sickle::load(ccl_content).context("Failed to parse CCL with sickle")?;
52
53 model_to_hashmap(&model)
55}
56
57fn model_to_hashmap(model: &sickle::CclObject) -> Result<HashMap<String, Value>> {
59 let mut result = HashMap::new();
60
61 for (key, value) in model.iter() {
62 result.insert(key.clone(), model_to_value(value)?);
63 }
64
65 Ok(result)
66}
67
68fn model_to_value(model: &sickle::CclObject) -> Result<Value> {
70 if let Ok(empty_key_values) = model.get_all("") {
73 if !empty_key_values.is_empty() {
74 let all_simple_strings = empty_key_values
76 .iter()
77 .all(|v| v.len() == 1 && v.values().all(|child| child.is_empty()));
78
79 if all_simple_strings {
80 let values: Vec<Value> = empty_key_values
82 .iter()
83 .filter_map(|v| v.keys().next().cloned())
84 .map(Value::String)
85 .collect();
86 return Ok(Value::Array(values));
87 } else {
88 let values: Vec<Value> = empty_key_values
90 .iter()
91 .map(model_to_value)
92 .collect::<Result<Vec<_>>>()?;
93 return Ok(Value::Array(values));
94 }
95 }
96 }
97
98 if model.len() == 1 {
100 let (key, value) = model.iter().next().unwrap();
101
102 if value.is_empty() {
104 return Ok(Value::String(key.clone()));
105 }
106 }
107
108 if model.len() > 1 && model.values().all(|v| v.is_empty()) {
110 let values: Vec<Value> = model.keys().map(|k| Value::String(k.clone())).collect();
112 return Ok(Value::Array(values));
113 }
114
115 let mut obj = serde_json::Map::new();
117 for (k, v) in model.iter() {
118 obj.insert(k.clone(), model_to_value(v)?);
119 }
120 Ok(Value::Object(obj))
121}
122
123pub fn parse_ccl_to<T: DeserializeOwned>(ccl_content: &str) -> Result<T> {
149 sickle::from_str(ccl_content).context("Failed to deserialize parsed CCL")
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn test_parse_simple_array() {
159 let ccl = r#"
160test_pkg =
161 = brew
162 = scoop
163 = pacman
164"#;
165 let result = parse_to_hashmap(ccl).unwrap();
166
167 assert!(result.contains_key("test_pkg"));
168 let value = &result["test_pkg"];
169 println!("DEBUG test_pkg value: {:#?}", value);
170 assert!(value.is_array());
171
172 let arr = value.as_array().unwrap();
173 assert_eq!(arr.len(), 3);
174 assert_eq!(arr[0].as_str().unwrap(), "brew");
175 assert_eq!(arr[1].as_str().unwrap(), "scoop");
176 assert_eq!(arr[2].as_str().unwrap(), "pacman");
177 }
178
179 #[test]
180 fn test_parse_complex_object() {
181 let ccl = r#"
182test_pkg =
183 _sources =
184 = brew
185 = scoop
186 brew = gh
187"#;
188 let result = parse_to_hashmap(ccl).unwrap();
189
190 assert!(result.contains_key("test_pkg"));
191 let value = &result["test_pkg"];
192 println!("Parsed value: {:#?}", value);
193 assert!(value.is_object());
194
195 let obj = value.as_object().unwrap();
196 println!("Object keys: {:?}", obj.keys().collect::<Vec<_>>());
197 assert!(obj.contains_key("_sources"));
198 assert!(obj.contains_key("brew"));
199
200 let sources_value = &obj["_sources"];
201 println!("_sources value: {:#?}", sources_value);
202 let sources = sources_value.as_array().unwrap();
203 assert_eq!(sources.len(), 2);
204
205 let brew_override = obj["brew"].as_str().unwrap();
206 assert_eq!(brew_override, "gh");
207 }
208
209 #[test]
210 fn test_parse_multiple_packages() {
211 let ccl = r#"
212simple =
213 = brew
214 = scoop
215
216complex =
217 _sources =
218 = pacman
219 _platforms =
220 = linux
221"#;
222 let result = parse_to_hashmap(ccl).unwrap();
223
224 assert_eq!(result.len(), 2);
225 assert!(result["simple"].is_array());
226 assert!(result["complex"].is_object());
227 }
228}