package_parser/pkgs/
fortran.rs1use 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}