Skip to main content

package_parser/pkgs/java/
gradlelock.rs

1use packageurl::PackageUrl;
2use crate::types::DependentPackage;
3
4use crate::error::SourcePkgError;
5use crate::pkgs::common::model::{Package, PackageManifest};
6
7use std::fs::File;
8use std::io::{self, BufRead};
9use std::path::Path;
10
11fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
12where
13    P: AsRef<Path>,
14{
15    let file = File::open(filename)?;
16    Ok(io::BufReader::new(file).lines())
17}
18
19fn is_gradle_lockfile_depline(line: &str) -> bool {
20    let ret = line.starts_with('#') || line.starts_with("empty=");
21    !ret
22}
23
24fn parse_to_gradle_dependency(line: &str) -> Option<DependentPackage> {
25    let parts: Vec<&str> = line.splitn(3, ':').collect();
26    if parts.len() < 3 {
27        return None;
28    }
29
30    let group = parts[0];
31    let artifact = parts[1];
32    let (version, configurations) =
33        if let Some((version, configurations)) = parts[2].split_once('=') {
34            (Some(version), Some(configurations))
35        } else {
36            (None, None)
37        };
38
39    let is_runtime = configurations
40        .map(|c| {
41            c.contains("runtime")
42                || c.contains("Runtime")
43                || c.contains("compile")
44                || c.contains("Compile")
45        })
46        .unwrap_or(false);
47
48    let mut purl = PackageUrl::new("maven", artifact).unwrap();
49    purl.with_namespace(group);
50    if let Some(version) = version {
51        purl.with_version(version);
52    };
53
54    Some(DependentPackage {
55        purl: purl.to_string(),
56        is_resolved: true,
57        requirement: version.unwrap_or_default().to_string(),
58        is_runtime,
59        ..Default::default()
60    })
61}
62
63pub struct GradleLock {}
64
65impl GradleLock {
66    pub fn new() -> Self {
67        Self {}
68    }
69
70    fn parse_gradle_lock(path: impl AsRef<Path>) -> Result<Package, SourcePkgError> {
71        let mut contents = vec![];
72        if let Ok(lines) = read_lines(path) {
73            lines.for_each(|line| {
74                if let Ok(ip) = line {
75                    contents.push(ip);
76                }
77            });
78        }
79
80        let mut package = Package {
81            ..Default::default()
82        };
83
84        for line in contents {
85            let line = line.trim();
86            if !is_gradle_lockfile_depline(line) {
87                continue;
88            }
89
90            if let Some(dep) = parse_to_gradle_dependency(line) {
91                package.dependencies.push(dep);
92            };
93        }
94
95        Ok(package)
96    }
97}
98
99#[async_trait::async_trait]
100impl PackageManifest for GradleLock {
101    fn get_name(&self) -> String {
102        "maven".into()
103    }
104
105    async fn recognize(&self, path: &Path) -> Result<Package, SourcePkgError> {
106        Self::parse_gradle_lock(path)
107    }
108
109    fn file_name_patterns(&self) -> &'static [&'static str] {
110        &["gradle.lockfile"]
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn parse_gradle_dep() {
120        let filepath = Path::new(concat!(
121            env!("CARGO_MANIFEST_DIR"),
122            "/testdata/gradle_lock/5-pkg"
123        ));
124
125        let package = GradleLock::parse_gradle_lock(filepath).unwrap();
126        println!("{:#?}", package);
127    }
128
129    #[test]
130    fn parse_gradle_lockfile() {
131        let filepath = Path::new(concat!(
132            env!("CARGO_MANIFEST_DIR"),
133            "/testdata/gradle_lock/gradle.lockfile"
134        ));
135
136        let package = GradleLock::parse_gradle_lock(filepath).unwrap();
137        println!("{:#?}", package);
138    }
139}