Skip to main content

normalize_manifest/
gleam.rs

1//! Parser for `gleam.toml` files (Gleam package manager).
2
3use crate::{DeclaredDep, DepKind, ManifestError, ManifestParser, ParsedManifest};
4use toml::Value;
5
6/// Parser for `gleam.toml` files.
7///
8/// `[dependencies]` → `Normal`, `[dev-dependencies]` → `Dev`.
9/// Version values are used as-is (e.g., `">= 0.34.0 and < 2.0.0"`).
10pub struct GleamParser;
11
12impl ManifestParser for GleamParser {
13    fn filename(&self) -> &'static str {
14        "gleam.toml"
15    }
16
17    fn parse(&self, content: &str) -> Result<ParsedManifest, ManifestError> {
18        let toml: Value = content
19            .parse::<Value>()
20            .map_err(|e| ManifestError(e.to_string()))?;
21
22        let name = toml
23            .get("name")
24            .and_then(|v| v.as_str())
25            .map(|s| s.to_string());
26        let version = toml
27            .get("version")
28            .and_then(|v| v.as_str())
29            .map(|s| s.to_string());
30
31        let mut deps = Vec::new();
32        extract_gleam_deps(&toml, "dependencies", DepKind::Normal, &mut deps);
33        extract_gleam_deps(&toml, "dev-dependencies", DepKind::Dev, &mut deps);
34
35        Ok(ParsedManifest {
36            ecosystem: "gleam",
37            name,
38            version,
39            dependencies: deps,
40        })
41    }
42}
43
44fn extract_gleam_deps(toml: &Value, section: &str, kind: DepKind, out: &mut Vec<DeclaredDep>) {
45    let Some(table) = toml.get(section).and_then(|v| v.as_table()) else {
46        return;
47    };
48    for (name, val) in table {
49        let version_req = val.as_str().map(|s| s.to_string());
50        out.push(DeclaredDep {
51            name: name.clone(),
52            version_req,
53            kind,
54        });
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use crate::ManifestParser;
62
63    #[test]
64    fn test_gleam_toml() {
65        let content = r#"
66name = "my_project"
67version = "0.1.0"
68
69[dependencies]
70gleam_stdlib = ">= 0.34.0 and < 2.0.0"
71lustre = ">= 4.0.0 and < 5.0.0"
72
73[dev-dependencies]
74gleeunit = ">= 1.0.0 and < 2.0.0"
75"#;
76        let m = GleamParser.parse(content).unwrap();
77        assert_eq!(m.ecosystem, "gleam");
78        assert_eq!(m.name.as_deref(), Some("my_project"));
79        assert_eq!(m.version.as_deref(), Some("0.1.0"));
80        assert_eq!(m.dependencies.len(), 3);
81
82        let stdlib = m
83            .dependencies
84            .iter()
85            .find(|d| d.name == "gleam_stdlib")
86            .unwrap();
87        assert_eq!(stdlib.kind, DepKind::Normal);
88        assert_eq!(stdlib.version_req.as_deref(), Some(">= 0.34.0 and < 2.0.0"));
89
90        let lustre = m.dependencies.iter().find(|d| d.name == "lustre").unwrap();
91        assert_eq!(lustre.kind, DepKind::Normal);
92
93        let gleeunit = m
94            .dependencies
95            .iter()
96            .find(|d| d.name == "gleeunit")
97            .unwrap();
98        assert_eq!(gleeunit.kind, DepKind::Dev);
99        assert_eq!(
100            gleeunit.version_req.as_deref(),
101            Some(">= 1.0.0 and < 2.0.0")
102        );
103    }
104
105    #[test]
106    fn test_gleam_no_dev_deps() {
107        let content = r#"
108name = "lib"
109version = "0.2.0"
110
111[dependencies]
112gleam_stdlib = ">= 0.30.0 and < 2.0.0"
113"#;
114        let m = GleamParser.parse(content).unwrap();
115        assert_eq!(m.dependencies.len(), 1);
116        assert_eq!(m.dependencies[0].kind, DepKind::Normal);
117    }
118}