Skip to main content

provenant/parsers/maven/
mod.rs

1// SPDX-FileCopyrightText: Provenant contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Parser for Apache Maven pom.xml files.
5//!
6//! Extracts package metadata, dependencies, and license information from
7//! Maven Project Object Model (POM) files.
8//!
9//! # Supported Formats
10//! - pom.xml (Project Object Model)
11//! - pom.properties
12//! - MANIFEST.MF (JAR manifest)
13//!
14//! # Key Features
15//! - Property value substitution (`${project.version}`)
16//! - `is_pinned` analysis (exact version vs ranges like `[1.0,2.0)`)
17//! - Dependency scope handling (compile, test, provided, runtime, system)
18//! - Package URL (purl) generation
19//! - Multiple license support (combined with " OR ")
20
21mod coordinates;
22mod manifest;
23mod pom;
24mod properties;
25
26#[cfg(test)]
27mod manifest_test;
28#[cfg(test)]
29mod pom_test;
30#[cfg(test)]
31mod properties_test;
32#[cfg(test)]
33mod scan_test;
34
35use self::{manifest::parse_manifest_mf, pom::parse_pom_xml, properties::parse_pom_properties};
36use super::PackageParser;
37use super::metadata::ParserMetadata;
38use crate::models::{DatasourceId, PackageData, PackageType};
39use std::path::Path;
40
41/// Maven package parser supporting pom.xml, pom.properties, and MANIFEST.MF files.
42pub struct MavenParser;
43
44impl PackageParser for MavenParser {
45    const PACKAGE_TYPE: PackageType = PackageType::Maven;
46
47    fn metadata() -> Vec<ParserMetadata> {
48        vec![ParserMetadata {
49            description: "Apache Maven POM",
50            file_patterns: &[
51                "**/*.pom",
52                "**/pom.xml",
53                "**/pom.properties",
54                "**/META-INF/MANIFEST.MF",
55            ],
56            package_type: "maven",
57            primary_language: "Java",
58            documentation_url: Some("https://maven.apache.org/pom.html"),
59        }]
60    }
61
62    fn extract_packages(path: &Path) -> Vec<PackageData> {
63        if let Some(filename) = path.file_name().and_then(|name| name.to_str()) {
64            if filename == "pom.properties" {
65                return vec![parse_pom_properties(path)];
66            }
67            if filename == "MANIFEST.MF" {
68                return vec![parse_manifest_mf(path)];
69            }
70        }
71
72        parse_pom_xml(path)
73    }
74
75    fn is_match(path: &Path) -> bool {
76        if let Some(filename) = path.file_name().and_then(|name| name.to_str()) {
77            filename == "pom.xml"
78                || filename.ends_with(".pom.xml")
79                || filename.ends_with("-pom.xml")
80                || filename.ends_with("_pom.xml")
81                || filename == "pom.properties"
82                || filename == "MANIFEST.MF"
83                || filename.ends_with(".pom")
84        } else {
85            false
86        }
87    }
88}
89
90fn default_package_data(datasource_id: DatasourceId) -> PackageData {
91    PackageData {
92        package_type: Some(PackageType::Maven),
93        datasource_id: Some(datasource_id),
94        ..Default::default()
95    }
96}