cargo_toml_parser/
lib.rs

1use std::collections::BTreeMap;
2
3use serde::Deserialize;
4
5#[derive(Debug, Deserialize)]
6pub struct CargoToml {
7    pub package: Package,
8    pub dependencies: Option<BTreeMap<String, Dependency>>,
9}
10
11#[derive(Debug, Deserialize)]
12pub struct Package {
13    pub name: String,
14    pub version: String,
15}
16
17#[derive(Debug, Deserialize)]
18pub struct Dependency {
19    pub version: String,
20}
21
22impl CargoToml {
23    pub fn get_dependency(&self, name: &str) -> Option<&Dependency> {
24        self.dependencies.as_ref().and_then(|deps| deps.get(name))
25    }
26}
27
28pub fn parse_cargo_toml(toml: &str) -> Result<CargoToml, String> {
29    #[derive(Deserialize)]
30    struct CargoTomlVisitor {
31        package: Package,
32        dependencies: Option<BTreeMap<String, toml::Value>>,
33    }
34
35    let parsed = toml::from_str::<CargoTomlVisitor>(toml).map_err(|e| e.to_string())?;
36
37    let dependencies = parsed
38        .dependencies
39        .map(parse_dependencies)
40        .map_or(Ok(None), |v| v.map(Some))?;
41
42    Ok(CargoToml {
43        package: parsed.package,
44        dependencies,
45    })
46}
47
48fn parse_dependencies(
49    deps: BTreeMap<String, toml::Value>,
50) -> Result<BTreeMap<String, Dependency>, String> {
51    let mut dependencies = BTreeMap::new();
52    for (name, value) in deps {
53        if value.is_str() {
54            let version = value.as_str().unwrap();
55            dependencies.insert(
56                name,
57                Dependency {
58                    version: version.to_string(),
59                },
60            );
61        } else if value.is_table() {
62            if let Some(version) = value.get("version") {
63                dependencies.insert(
64                    name,
65                    Dependency {
66                        version: version
67                            .as_str()
68                            .ok_or("can't parse version as str".to_string())?
69                            .to_string(),
70                    },
71                );
72            } else if let Some(tag) = value.get("tag") {
73                dependencies.insert(
74                    name,
75                    Dependency {
76                        version: tag
77                            .as_str()
78                            .ok_or("can't parse tag as str".to_string())?
79                            .to_string(),
80                    },
81                );
82            } else {
83                return Err("can't find version or path".to_string());
84            }
85        }
86    }
87    Ok(dependencies)
88}
89
90#[cfg(test)]
91mod test {
92    use super::*;
93    #[test]
94    fn test_parse_cargo_toml() {
95        let toml = r#"
96            [package]
97            name = "hello"
98            version = "0.1.0"
99            [dependencies]
100            serde = "1.0"
101            toml = { version = "0.5" }
102            hyle = { git = "https://github.com/Hyle-org/hyle", tag = "0.12" }
103            "#;
104        let cargo_toml = parse_cargo_toml(toml).unwrap();
105        assert_eq!(cargo_toml.package.name, "hello");
106        assert_eq!(cargo_toml.package.version, "0.1.0");
107        assert_eq!(cargo_toml.dependencies.as_ref().unwrap().len(), 3);
108
109        assert_eq!(cargo_toml.get_dependency("serde").unwrap().version, "1.0");
110        assert_eq!(cargo_toml.get_dependency("toml").unwrap().version, "0.5");
111        assert_eq!(cargo_toml.get_dependency("hyle").unwrap().version, "0.12");
112    }
113}
114