package_parser/pkgs/java/
gradle_dependency.rs1use 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
29pub 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