Skip to main content

provenant/parsers/debian/
tarball.rs

1use std::path::Path;
2
3use crate::models::{DatasourceId, PackageData, PackageType};
4use crate::parsers::utils::truncate_field;
5
6use super::utils::build_debian_purl;
7use super::{PACKAGE_TYPE, default_package_data};
8use crate::parsers::PackageParser;
9
10/// Parser for Debian original source tarballs (*.orig.tar.*)
11pub struct DebianOrigTarParser;
12
13impl PackageParser for DebianOrigTarParser {
14    const PACKAGE_TYPE: PackageType = PACKAGE_TYPE;
15
16    fn is_match(path: &Path) -> bool {
17        path.file_name()
18            .and_then(|n| n.to_str())
19            .map(|name| name.contains(".orig.tar."))
20            .unwrap_or(false)
21    }
22
23    fn extract_packages(path: &Path) -> Vec<PackageData> {
24        let filename = match path.file_name().and_then(|n| n.to_str()) {
25            Some(f) => f,
26            None => {
27                return vec![default_package_data(
28                    DatasourceId::DebianOriginalSourceTarball,
29                )];
30            }
31        };
32
33        vec![parse_source_tarball_filename(
34            filename,
35            DatasourceId::DebianOriginalSourceTarball,
36        )]
37    }
38}
39
40crate::register_parser!(
41    "Debian original source tarball",
42    &["**/*.orig.tar.*"],
43    "deb",
44    "",
45    Some("https://www.debian.org/doc/debian-policy/ch-source.html"),
46);
47
48/// Parser for Debian source package metadata tarballs (*.debian.tar.*)
49pub struct DebianDebianTarParser;
50
51impl PackageParser for DebianDebianTarParser {
52    const PACKAGE_TYPE: PackageType = PACKAGE_TYPE;
53
54    fn is_match(path: &Path) -> bool {
55        path.file_name()
56            .and_then(|n| n.to_str())
57            .map(|name| name.contains(".debian.tar."))
58            .unwrap_or(false)
59    }
60
61    fn extract_packages(path: &Path) -> Vec<PackageData> {
62        let filename = match path.file_name().and_then(|n| n.to_str()) {
63            Some(f) => f,
64            None => {
65                return vec![default_package_data(
66                    DatasourceId::DebianSourceMetadataTarball,
67                )];
68            }
69        };
70
71        vec![parse_source_tarball_filename(
72            filename,
73            DatasourceId::DebianSourceMetadataTarball,
74        )]
75    }
76}
77
78crate::register_parser!(
79    "Debian source metadata tarball",
80    &["**/*.debian.tar.*"],
81    "deb",
82    "",
83    Some("https://www.debian.org/doc/debian-policy/ch-source.html"),
84);
85
86fn parse_source_tarball_filename(filename: &str, datasource_id: DatasourceId) -> PackageData {
87    let without_tar_ext = filename
88        .trim_end_matches(".gz")
89        .trim_end_matches(".xz")
90        .trim_end_matches(".bz2")
91        .trim_end_matches(".tar");
92
93    let parts: Vec<&str> = without_tar_ext.splitn(2, '_').collect();
94    if parts.len() < 2 {
95        return default_package_data(datasource_id);
96    }
97
98    let name = truncate_field(parts[0].to_string());
99    let version_with_suffix = parts[1];
100
101    let version = version_with_suffix
102        .trim_end_matches(".orig")
103        .trim_end_matches(".debian")
104        .to_string();
105    let version = truncate_field(version);
106
107    let namespace = Some("debian".to_string());
108
109    PackageData {
110        datasource_id: Some(datasource_id),
111        package_type: Some(PACKAGE_TYPE),
112        namespace: namespace.clone(),
113        name: Some(name.clone()),
114        version: Some(version.clone()),
115        purl: build_debian_purl(&name, Some(&version), namespace.as_deref(), None),
116        ..Default::default()
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use crate::models::DatasourceId;
124    use std::path::PathBuf;
125
126    #[test]
127    fn test_orig_tar_parser_is_match() {
128        assert!(DebianOrigTarParser::is_match(&PathBuf::from(
129            "package_1.0.orig.tar.gz"
130        )));
131        assert!(DebianOrigTarParser::is_match(&PathBuf::from(
132            "abseil_0~20200923.3.orig.tar.xz"
133        )));
134        assert!(!DebianOrigTarParser::is_match(&PathBuf::from(
135            "package.debian.tar.gz"
136        )));
137        assert!(!DebianOrigTarParser::is_match(&PathBuf::from("control")));
138    }
139
140    #[test]
141    fn test_debian_tar_parser_is_match() {
142        assert!(DebianDebianTarParser::is_match(&PathBuf::from(
143            "package_1.0-1.debian.tar.xz"
144        )));
145        assert!(DebianDebianTarParser::is_match(&PathBuf::from(
146            "abseil_20220623.1-1.debian.tar.gz"
147        )));
148        assert!(!DebianDebianTarParser::is_match(&PathBuf::from(
149            "package.orig.tar.gz"
150        )));
151        assert!(!DebianDebianTarParser::is_match(&PathBuf::from("control")));
152    }
153
154    #[test]
155    fn test_parse_orig_tar_filename() {
156        let pkg = parse_source_tarball_filename(
157            "abseil_0~20200923.3.orig.tar.gz",
158            DatasourceId::DebianOriginalSourceTarball,
159        );
160        assert_eq!(pkg.name, Some("abseil".to_string()));
161        assert_eq!(pkg.version, Some("0~20200923.3".to_string()));
162        assert_eq!(pkg.namespace, Some("debian".to_string()));
163        assert_eq!(
164            pkg.purl,
165            Some("pkg:deb/debian/abseil@0~20200923.3".to_string())
166        );
167        assert_eq!(
168            pkg.datasource_id,
169            Some(DatasourceId::DebianOriginalSourceTarball)
170        );
171    }
172
173    #[test]
174    fn test_parse_debian_tar_filename() {
175        let pkg = parse_source_tarball_filename(
176            "abseil_20220623.1-1.debian.tar.xz",
177            DatasourceId::DebianSourceMetadataTarball,
178        );
179        assert_eq!(pkg.name, Some("abseil".to_string()));
180        assert_eq!(pkg.version, Some("20220623.1-1".to_string()));
181        assert_eq!(pkg.namespace, Some("debian".to_string()));
182        assert_eq!(
183            pkg.purl,
184            Some("pkg:deb/debian/abseil@20220623.1-1".to_string())
185        );
186    }
187
188    #[test]
189    fn test_parse_source_tarball_various_compressions() {
190        let pkg_gz = parse_source_tarball_filename(
191            "test_1.0.orig.tar.gz",
192            DatasourceId::DebianOriginalSourceTarball,
193        );
194        let pkg_xz = parse_source_tarball_filename(
195            "test_1.0.orig.tar.xz",
196            DatasourceId::DebianOriginalSourceTarball,
197        );
198        let pkg_bz2 = parse_source_tarball_filename(
199            "test_1.0.orig.tar.bz2",
200            DatasourceId::DebianOriginalSourceTarball,
201        );
202
203        assert_eq!(pkg_gz.version, Some("1.0".to_string()));
204        assert_eq!(pkg_xz.version, Some("1.0".to_string()));
205        assert_eq!(pkg_bz2.version, Some("1.0".to_string()));
206    }
207
208    #[test]
209    fn test_parse_source_tarball_invalid_format() {
210        let pkg = parse_source_tarball_filename(
211            "invalid-no-underscore.tar.gz",
212            DatasourceId::DebianOriginalSourceTarball,
213        );
214        assert!(pkg.name.is_none());
215        assert!(pkg.version.is_none());
216    }
217}