Skip to main content

package_parser/pkgs/java/
gradle_dependency.rs

1use nom::sequence::preceded;
2use nom::{
3    branch::alt,
4    bytes::complete::{tag, take_until},
5    character::complete::not_line_ending,
6    combinator::recognize,
7    error::VerboseError,
8    sequence::delimited,
9    IResult,
10};
11use packageurl::PackageUrl;
12use crate::types::DependentPackage;
13
14use crate::error::SourcePkgError;
15use crate::pkgs::common::model::{Package, PackageManifest};
16
17use std::fs::File;
18use std::io::Read;
19use std::path::Path;
20
21type NomResult<T, U> = IResult<T, U, VerboseError<T>>;
22
23#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Debug)]
24pub struct PackageDescriptor {
25    pub name: String,
26    pub version: String,
27}
28
29// https://github.com/phylum-dev/cli/blob/ecd02687f694356f3c71630d40cff73b9e84084a/cli/tests/fixtures/gradle-dependencies.txt
30
31pub fn parse(input: &str) -> NomResult<&str, Vec<PackageDescriptor>> {
32    let pkgs = input.lines().filter_map(package).collect::<Vec<_>>();
33    Ok((input, pkgs))
34}
35
36fn group_id(input: &str) -> NomResult<&str, &str> {
37    recognize(take_until(":"))(input)
38}
39
40fn artifact_id_version(input: &str) -> NomResult<&str, &str> {
41    let (input, artifact_id) = delimited(tag(":"), take_until(":"), tag(":"))(input)?;
42    let (_, version) = recognize(alt((take_until(" ("), not_line_ending)))(input)?;
43    Ok((artifact_id, version))
44}
45
46fn filter_line(input: &str) -> NomResult<&str, &str> {
47    let (input, _) = recognize(alt((
48        take_until("+---"),
49        take_until("\\---"),
50        not_line_ending,
51    )))(input)?;
52    preceded(alt((tag("+--- "), tag("\\--- "))), not_line_ending)(input)
53}
54
55fn package(input: &str) -> Option<PackageDescriptor> {
56    let (_, input) = filter_line(input).ok()?;
57    let (input, group_id) = group_id(input).ok()?;
58    let (artifact_id, version) = artifact_id_version(input).ok()?;
59
60    Some(PackageDescriptor {
61        name: format!("{}:{}", group_id, artifact_id),
62        version: version.to_string(),
63    })
64}
65
66pub struct GradleDependencies {}
67
68impl GradleDependencies {
69    pub fn new() -> Self {
70        Self {}
71    }
72
73    fn parse_gradle_lock(path: impl AsRef<Path>) -> Result<Package, SourcePkgError> {
74        let mut file = File::open(path)?;
75        let mut content = String::new();
76        file.read_to_string(&mut content)?;
77        let (_, requirements) =
78            parse(&content).map_err(|e| SourcePkgError::GenericsError2(e.to_string()))?;
79
80        let requirements = requirements
81            .into_iter()
82            .map(|desc| DependentPackage {
83                purl: PackageUrl::new("maven", desc.name)
84                    .expect("purl arguments are invalid")
85                    .to_string(),
86                requirement: desc.version,
87                is_resolved: true,
88                ..Default::default()
89            })
90            .collect::<Vec<_>>();
91
92        let package = Package {
93            dependencies: requirements,
94            ..Default::default()
95        };
96
97        Ok(package)
98    }
99}
100
101#[async_trait::async_trait]
102impl PackageManifest for GradleDependencies {
103    fn get_name(&self) -> String {
104        "maven".into()
105    }
106
107    async fn recognize(&self, path: &Path) -> Result<Package, SourcePkgError> {
108        Self::parse_gradle_lock(path)
109    }
110
111    fn file_name_patterns(&self) -> &'static [&'static str] {
112        &["gradle-dependencies.txt", "dependencies.txt"]
113    }
114}
115