normalize_manifest/
elm.rs1use crate::{DeclaredDep, DepKind, ManifestError, ManifestParser, ParsedManifest};
8use serde_json::Value;
9
10pub struct ElmParser;
16
17impl ManifestParser for ElmParser {
18 fn filename(&self) -> &'static str {
19 "elm.json"
20 }
21
22 fn parse(&self, content: &str) -> Result<ParsedManifest, ManifestError> {
23 let json: Value =
24 serde_json::from_str(content).map_err(|e| ManifestError(e.to_string()))?;
25
26 let elm_type = json.get("type").and_then(|v| v.as_str());
27 let is_application = elm_type == Some("application");
28
29 let name = json
31 .get("name")
32 .and_then(|v| v.as_str())
33 .map(|s| s.to_string());
34 let version = json
35 .get("version")
36 .and_then(|v| v.as_str())
37 .map(|s| s.to_string());
38
39 let mut deps = Vec::new();
40
41 let dep_value = json.get("dependencies");
42 if is_application {
43 if let Some(direct) = dep_value
45 .and_then(|v| v.get("direct"))
46 .and_then(|v| v.as_object())
47 {
48 for (pkg, ver) in direct {
49 deps.push(DeclaredDep {
50 name: pkg.clone(),
51 version_req: ver.as_str().map(|s| s.to_string()),
52 kind: DepKind::Normal,
53 });
54 }
55 }
56 } else {
57 if let Some(obj) = dep_value.and_then(|v| v.as_object()) {
59 for (pkg, ver) in obj {
60 deps.push(DeclaredDep {
61 name: pkg.clone(),
62 version_req: ver.as_str().map(|s| s.to_string()),
63 kind: DepKind::Normal,
64 });
65 }
66 }
67 }
68
69 if let Some(test_deps) = json.get("test-dependencies") {
71 let direct_obj = test_deps.get("direct").and_then(|v| v.as_object());
73 let indirect_obj = test_deps.get("indirect").and_then(|v| v.as_object());
74
75 if direct_obj.is_some() || indirect_obj.is_some() {
76 for obj in [direct_obj, indirect_obj].into_iter().flatten() {
77 for (pkg, ver) in obj {
78 deps.push(DeclaredDep {
79 name: pkg.clone(),
80 version_req: ver.as_str().map(|s| s.to_string()),
81 kind: DepKind::Dev,
82 });
83 }
84 }
85 } else if let Some(obj) = test_deps.as_object() {
86 for (pkg, ver) in obj {
87 deps.push(DeclaredDep {
88 name: pkg.clone(),
89 version_req: ver.as_str().map(|s| s.to_string()),
90 kind: DepKind::Dev,
91 });
92 }
93 }
94 }
95
96 Ok(ParsedManifest {
97 ecosystem: "elm",
98 name,
99 version,
100 dependencies: deps,
101 })
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use crate::ManifestParser;
109
110 #[test]
111 fn test_elm_application() {
112 let content = r#"{
113 "type": "application",
114 "source-directories": ["src"],
115 "elm-version": "0.19.1",
116 "dependencies": {
117 "direct": {
118 "elm/core": "1.0.5",
119 "elm/html": "1.0.0"
120 },
121 "indirect": {
122 "elm/json": "1.1.3"
123 }
124 },
125 "test-dependencies": {
126 "direct": {
127 "elm-explorations/test": "2.1.2"
128 },
129 "indirect": {}
130 }
131}"#;
132 let m = ElmParser.parse(content).unwrap();
133 assert_eq!(m.ecosystem, "elm");
134
135 let normal: Vec<_> = m
136 .dependencies
137 .iter()
138 .filter(|d| d.kind == DepKind::Normal)
139 .collect();
140 assert_eq!(normal.len(), 2);
141 assert!(normal.iter().any(|d| d.name == "elm/core"));
142 assert!(normal.iter().any(|d| d.name == "elm/html"));
143
144 let dev: Vec<_> = m
145 .dependencies
146 .iter()
147 .filter(|d| d.kind == DepKind::Dev)
148 .collect();
149 assert_eq!(dev.len(), 1);
150 assert_eq!(dev[0].name, "elm-explorations/test");
151 assert_eq!(dev[0].version_req.as_deref(), Some("2.1.2"));
152 }
153
154 #[test]
155 fn test_elm_package() {
156 let content = r#"{
157 "type": "package",
158 "name": "elm/html",
159 "summary": "Fast HTML",
160 "version": "1.0.0",
161 "elm-version": "0.19.0 <= v < 0.20.0",
162 "exposed-modules": ["Html"],
163 "dependencies": {
164 "elm/core": "1.0.0 <= v < 2.0.0",
165 "elm/virtual-dom": "1.0.0 <= v < 2.0.0"
166 },
167 "test-dependencies": {
168 "elm-explorations/test": "1.0.0 <= v < 2.0.0"
169 }
170}"#;
171 let m = ElmParser.parse(content).unwrap();
172 assert_eq!(m.ecosystem, "elm");
173 assert_eq!(m.name.as_deref(), Some("elm/html"));
174 assert_eq!(m.version.as_deref(), Some("1.0.0"));
175
176 let normal: Vec<_> = m
177 .dependencies
178 .iter()
179 .filter(|d| d.kind == DepKind::Normal)
180 .collect();
181 assert_eq!(normal.len(), 2);
182
183 let dev: Vec<_> = m
184 .dependencies
185 .iter()
186 .filter(|d| d.kind == DepKind::Dev)
187 .collect();
188 assert_eq!(dev.len(), 1);
189 assert_eq!(dev[0].name, "elm-explorations/test");
190 }
191}