Skip to main content

package_parser/pkgs/
fortran.rs

1use std::{collections::HashMap, path::Path};
2
3use maplit::hashset;
4use packageurl::PackageUrl;
5use crate::types::{DependentPackage, Package};
6use serde::Deserialize;
7
8use crate::{error::SourcePkgError, PackageManifest};
9
10#[derive(Deserialize, Debug, Default)]
11#[serde(rename_all = "kebab-case", default)]
12struct Manifest {
13    dependencies: HashMap<String, Dependency>,
14    dev_dependencies: HashMap<String, Dependency>,
15}
16
17#[derive(Deserialize, Debug)]
18#[serde(untagged)]
19#[allow(unused)]
20enum Dependency {
21    External {
22        git: Option<String>,
23        rev: Option<String>,
24        tag: Option<String>,
25    },
26    Builtin(String),
27}
28
29impl Dependency {
30    fn to_purl_and_version(&self, key: &str) -> (String, String) {
31        let mut purl = PackageUrl::new("fpm", key).unwrap();
32
33        let version = match self {
34            Dependency::External { git, rev, tag } => {
35                if let Some(git) = git {
36                    purl.add_qualifier("vcs_url", git).unwrap();
37                }
38                if let Some(rev) = rev {
39                    rev.clone()
40                } else if let Some(tag) = tag {
41                    tag.clone()
42                } else {
43                    "*".to_string()
44                }
45            }
46            Dependency::Builtin(_) => "*".to_string(),
47        };
48
49        if version != "*" {
50            purl.with_version(&version);
51        }
52
53        (purl.to_string(), version)
54    }
55}
56
57pub struct FpmToml {}
58
59impl FpmToml {
60    pub fn new() -> Self {
61        Self {}
62    }
63
64    fn parse(path: &Path) -> Result<Package, SourcePkgError> {
65        let content = std::fs::read_to_string(path).map_err(SourcePkgError::Io)?;
66
67        let parsed: Manifest = toml::from_str(&content).map_err(SourcePkgError::TomlDeserialize)?;
68
69        let mut deps = vec![];
70
71        for (key, dep) in parsed.dependencies {
72            let (purl, version) = dep.to_purl_and_version(&key);
73
74            deps.push(DependentPackage {
75                purl,
76                is_resolved: version != "*",
77                requirement: version,
78                scope: "prod".into(),
79                is_runtime: true,
80                relation: hashset! { crate::types::Relation::Direct },
81                ..Default::default()
82            });
83        }
84
85        for (key, dep) in parsed.dev_dependencies {
86            let (purl, version) = dep.to_purl_and_version(&key);
87
88            deps.push(DependentPackage {
89                purl,
90                is_resolved: version != "*",
91                requirement: version,
92                scope: "dev".into(),
93                is_runtime: false,
94                relation: hashset! { crate::types::Relation::Direct },
95                ..Default::default()
96            });
97        }
98
99        Ok(Package {
100            dependencies: deps,
101            ..Default::default()
102        })
103    }
104}
105
106#[async_trait::async_trait]
107impl PackageManifest for FpmToml {
108    fn get_name(&self) -> String {
109        "fortran".into()
110    }
111
112    async fn recognize(&self, path: &Path) -> Result<Package, SourcePkgError> {
113        Self::parse(path)
114    }
115
116    fn file_name_patterns(&self) -> &'static [&'static str] {
117        &["fpm.toml"]
118    }
119}
120
121#[cfg(test)]
122mod test {
123    use std::path::PathBuf;
124
125    use super::FpmToml;
126
127    #[test]
128    fn file1() {
129        let filepath = PathBuf::from(concat!(
130            env!("CARGO_MANIFEST_DIR"),
131            "/testdata/fortran/fpm-1.toml"
132        ));
133
134        dbg!(FpmToml::parse(&filepath).unwrap());
135    }
136
137    #[test]
138    fn file2() {
139        let filepath = PathBuf::from(concat!(
140            env!("CARGO_MANIFEST_DIR"),
141            "/testdata/fortran/fpm-2.toml"
142        ));
143
144        dbg!(FpmToml::parse(&filepath).unwrap());
145    }
146}