From a567506831d2226c1fd77d6cf671fa9da7d10c77 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Wed, 31 Dec 2025 19:23:49 +0100 Subject: [PATCH] package_info: refactor sources parsing with iterator --- src/package_info.rs | 128 ++++++++++++++++++++++++++------------------ 1 file changed, 76 insertions(+), 52 deletions(-) diff --git a/src/package_info.rs b/src/package_info.rs index 9a794cf..1290115 100644 --- a/src/package_info.rs +++ b/src/package_info.rs @@ -214,20 +214,32 @@ async fn get_components( Err("Components not found.".into()) } -/* -* Parse a 'Sources.gz' debian package file data, to look for a target package and -* return the data for that package stanza -*/ -fn parse_sources( - data: &[u8], - target_package: &str, - target_version: Option<&str>, -) -> Result, Box> { - let mut d = GzDecoder::new(data); - let mut s = String::new(); - d.read_to_string(&mut s)?; +struct DebianSources { + splitted_sources: std::str::Split<'static, &'static str>, +} +impl DebianSources { + fn new(data: &[u8]) -> Result> { + // Gz-decode 'Sources.gz' file into a string, and split it on stanzas + let mut d = GzDecoder::new(data); + let mut s = String::new(); + d.read_to_string(&mut s)?; - for stanza in s.split("\n\n") { + // Convert the string to a static lifetime by leaking it + let static_str = Box::leak(s.into_boxed_str()); + let splitted = static_str.split("\n\n"); + + Ok(DebianSources { + splitted_sources: splitted, + }) + } +} +impl Iterator for DebianSources { + type Item = HashMap; + + fn next(&mut self) -> Option { + let stanza = self.splitted_sources.next()?; + + // Parse stanza into a hashmap of strings, the fields let mut fields: HashMap = HashMap::new(); let mut current_key = String::new(); @@ -248,50 +260,62 @@ fn parse_sources( } } - if let Some(pkg) = fields.get("Package") - && pkg == target_package - { - // Check version if requested - if let Some(ver) = target_version { - if let Some(pkg_ver) = fields.get("Version") { - if pkg_ver != ver { - continue; - } - } else { - continue; - } - } + Some(fields) + } +} - let mut files = Vec::new(); - if let Some(checksums) = fields.get("Checksums-Sha256") { - for line in checksums.lines() { - let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.len() >= 3 { - files.push(FileEntry { - sha256: parts[0].to_string(), - size: parts[1].parse().unwrap_or(0), - name: parts[2].to_string(), - }); - } - } - } +/* +* Parse a 'Sources.gz' debian package file data, to look for a target package and +* return the data for that package stanza +*/ +fn parse_sources( + data: &[u8], + target_package: &str, + target_version: Option<&str>, +) -> Result, Box> { + let mut sources = DebianSources::new(data)?; - return Ok(Some(PackageStanza { - package: pkg.clone(), - version: fields.get("Version").cloned().unwrap_or_default(), - directory: fields.get("Directory").cloned().unwrap_or_default(), - format: fields - .get("Format") - .cloned() - .unwrap_or_else(|| "1.0".to_string()), - vcs_git: fields.get("Vcs-Git").cloned(), - vcs_browser: fields.get("Vcs-Browser").cloned(), - files, - })); + // Find the right package, with the right version if requested + let stanza = sources.find(|s| { + let pkg = s.get("Package"); + pkg.is_some() + && pkg.unwrap() == target_package + && (target_version.is_none() || s.get("Version").unwrap() == target_version.unwrap()) + }); + + if stanza.is_none() { + return Ok(None); + } + let stanza = stanza.unwrap(); + + // Parse package files + let mut files = Vec::new(); + if let Some(checksums) = stanza.get("Checksums-Sha256") { + for line in checksums.lines() { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 3 { + files.push(FileEntry { + sha256: parts[0].to_string(), + size: parts[1].parse().unwrap_or(0), + name: parts[2].to_string(), + }); + } } } - Ok(None) + // Return package stanza + Ok(Some(PackageStanza { + package: stanza.get("Package").cloned().unwrap(), + version: stanza.get("Version").cloned().unwrap_or_default(), + directory: stanza.get("Directory").cloned().unwrap_or_default(), + format: stanza + .get("Format") + .cloned() + .unwrap_or_else(|| "1.0".to_string()), + vcs_git: stanza.get("Vcs-Git").cloned(), + vcs_browser: stanza.get("Vcs-Browser").cloned(), + files, + })) } pub async fn get(