From 6185d5e664a4e89874fb37fe70663bfaf7f5f3f2 Mon Sep 17 00:00:00 2001 From: git-mann Date: Sun, 4 Oct 2026 22:36:05 -0400 Subject: [PATCH] Minor fixes for clippy lints - including some from pedantic group --- Cargo.toml | 27 +++++++++++++++++++++++++-- src/cmd_build.rs | 26 +++++++++++++------------- src/components/alpm.rs | 33 +++++++++++++++------------------ src/components/bigfiles.rs | 2 +- src/components/mod.rs | 6 +++--- src/components/rpm.rs | 38 +++++++++++++++++++------------------- src/packing.rs | 12 ++++++------ src/scan.rs | 8 ++++---- src/tar.rs | 9 +++++---- src/utils.rs | 15 +++++++-------- 10 files changed, 98 insertions(+), 78 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 289b7cc..ec920de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,9 +13,18 @@ publish = false [dependencies] anyhow = "1" camino = { version = "1", features = ["serde1"] } -cap-std-ext = { version = "5", default-features = false, features = ["fs_utf8"] } +cap-std-ext = { version = "5", default-features = false, features = [ + "fs_utf8", +] } chrono = "0.4" -clap = { version = "4", default-features = false, features = ["derive", "std", "help", "usage", "error-context", "env"] } +clap = { version = "4", default-features = false, features = [ + "derive", + "std", + "help", + "usage", + "error-context", + "env", +] } ctrlc = "3.5.2" flate2 = "1" fnv = "1.0.7" @@ -35,3 +44,17 @@ tracing-subscriber = { version = "0.3", features = ["env-filter"] } [dev-dependencies] fs-set-times = "0.20.3" maplit = "1" + +[lints.clippy] +cast_lossless = "deny" +manual_midpoint = "deny" +bool_to_int_with_if = "deny" +trivially_copy_pass_by_ref = "deny" +map_clone = "deny" +redundant_closure = "deny" +explicit_into_iter_loop = "deny" +unreadable_literal = "deny" +needless_raw_string_hashes = "deny" +unnecessary_wraps = "deny" +# This can have some false positives with things like `Arc` so only warning +needless_pass_by_value = "warn" diff --git a/src/cmd_build.rs b/src/cmd_build.rs index 0cf48ba..0f1f2b5 100644 --- a/src/cmd_build.rs +++ b/src/cmd_build.rs @@ -143,7 +143,7 @@ pub struct BuildArgs { impl BuildArgs { /// Apply CLI overrides to an OCI config, returning a new config. - fn apply_to_config(&self, config: oci_image::Config) -> Result { + fn apply_to_config(&self, config: &oci_image::Config) -> Result { let mut builder = oci_image::ConfigBuilder::default(); // Copy over all fields from base config. Would be nice if we could @@ -225,7 +225,7 @@ pub fn run(args: &BuildArgs) -> Result<()> { let annotations = parse_key_value_pairs(&args.annotations, parsed.annotations) .context("parsing annotations")?; - let image_config = build_image_config(args, parsed.config, created_epoch, architecture) + let image_config = build_image_config(args, &parsed.config, created_epoch, architecture) .context("building image config")?; let rootfs = Dir::open_ambient_dir(args.rootfs.as_std_path(), ambient_authority()) @@ -260,7 +260,7 @@ pub fn run(args: &BuildArgs) -> Result<()> { } // pack components down to max layers - let components = pack_components(args.max_layers, components).context("packing components")?; + let components = pack_components(args.max_layers, components); tracing::info!(layers = components.len(), "packing complete"); // build the OCI image @@ -319,7 +319,7 @@ pub fn run(args: &BuildArgs) -> Result<()> { /// Parse the `--output` value into an [`OutputTarget`]. fn parse_output_target(output: Option<&Utf8Path>) -> Result { - match output.map(|o| o.as_str()) { + match output.map(Utf8Path::as_str) { None => Ok(OutputTarget::Stdout), Some(s) => match s.split_once(':') { Some(("oci-archive", path)) => { @@ -380,7 +380,7 @@ fn write_manifest( file_count: component.files.len(), size: component.files.values().map(|f| f.size).sum(), stability: component.stability, - files: component.files.keys().map(|p| p.to_string()).collect(), + files: component.files.keys().map(Utf8PathBuf::to_string).collect(), }; (name.clone(), entry) }) @@ -475,7 +475,7 @@ fn resolve_created_epoch(source_date_epoch: Option, parsed: &ParsedConfig) /// Build the OCI image configuration from CLI args and a parsed config. fn build_image_config( args: &BuildArgs, - config: oci_image::Config, + config: &oci_image::Config, created: u64, architecture: &str, ) -> Result { @@ -539,7 +539,7 @@ fn parse_key_value_pairs( fn pack_components( max_layers: usize, components: HashMap, -) -> Result> { +) -> Vec<(String, Component)> { let mut entries: Vec> = components.into_iter().map(Some).collect(); // sort by component name for deterministic inputs to the packing algorithm entries.sort_by(|a, b| a.as_ref().unwrap().0.cmp(&b.as_ref().unwrap().0)); @@ -600,7 +600,7 @@ fn pack_components( } } - Ok(result) + result } #[cfg(test)] @@ -651,7 +651,7 @@ mod tests { // parse config from fixture file let parsed = parse_config(CONFIG_FIXTURE).unwrap(); - let image_config = build_image_config(&args, parsed.config, 1, "amd64").unwrap(); + let image_config = build_image_config(&args, &parsed.config, 1, "amd64").unwrap(); let rootfs = Dir::open_ambient_dir(rootfs_dir.path(), ambient_authority()).unwrap(); @@ -702,7 +702,7 @@ mod tests { // verify there's no history assert!( - image_config.history().as_ref().is_none_or(|h| h.is_empty()), + image_config.history().as_ref().is_none_or(Vec::is_empty), "image should have no history entries" ); @@ -861,7 +861,7 @@ mod tests { ..Default::default() }; - let image_config = build_image_config(&args, parsed.config, 1, "amd64").unwrap(); + let image_config = build_image_config(&args, &parsed.config, 1, "amd64").unwrap(); let labels = image_config .config() .as_ref() @@ -891,7 +891,7 @@ mod tests { let rootfs = Dir::open_ambient_dir(tmp.path(), ambient_authority()).unwrap(); let add_component = |name: &str, size_mb: usize, interval: &str| { - rootfs.write(name, &vec![0u8; size_mb * MB]).unwrap(); + rootfs.write(name, vec![0u8; size_mb * MB]).unwrap(); rootfs .setxattr(name, XATTR_COMPONENT, name.as_bytes()) .unwrap(); @@ -907,7 +907,7 @@ mod tests { let files = crate::scan::Scanner::new(&rootfs).scan().unwrap(); let repos = ComponentsRepos::load(&rootfs, &files, 0).unwrap(); let components = repos.into_components(&rootfs, files).unwrap(); - pack_components(2, components).unwrap() + pack_components(2, components) }; // Helper to find which packed layer contains a given file. diff --git a/src/components/alpm.rs b/src/components/alpm.rs index 2836a72..636bd41 100644 --- a/src/components/alpm.rs +++ b/src/components/alpm.rs @@ -357,7 +357,7 @@ impl ComponentsRepo for AlpmComponentsRepo { ) -> Vec { self.path_to_components .get(path) - .map(|components| components.to_vec()) + .cloned() .unwrap_or_default() } @@ -572,7 +572,7 @@ impl LocalAlpmDbFile { .get(section) .ok_or_else(|| anyhow!("section not found: {section}"))? .iter() - .map(|line| line.as_str()); + .map(String::as_str); let first = lines .next() .ok_or_else(|| anyhow!("no value found for section {section}"))?; @@ -590,7 +590,7 @@ impl LocalAlpmDbFile { /// Note that the spec is different for `alpm-db-desc` and `alpm-db-files` (see [`Self::get_single_line_value`]). /// If you are parsing a `alpm-db-files` file, you might need to filter additional newlines by yourself. fn get_multi_line_value(&self, section: &str) -> Option<&[String]> { - self.0.get(section).map(|value| value.as_slice()) + self.0.get(section).map(Vec::as_slice) } /// Checks that the given line is a well-formed header line and returns the section name if it is @@ -782,8 +782,8 @@ mod tests { // Multiple packages in base have gcc as their basename, so in terms of component names, they will be seen multiple times let mut expected_components = IMPORTANT_ARCH_LINUX_PACKAGES_IN_BASE - .into_iter() - .map(|pkg| *pkg) + .iter() + .copied() .collect::>(); let component_info = claims.iter().map(|claim| alpm.component_info(*claim)); for component in component_info { @@ -848,7 +848,7 @@ mod tests { #[test] fn test_parse_desc() { - const DESC_CONTENTS: &str = r#"%NAME% + const DESC_CONTENTS: &str = r"%NAME% filesystem %VERSION% @@ -889,7 +889,7 @@ iana-etc %XDATA% pkgtype=pkg -"#; +"; let parsed_desc = DESC_CONTENTS.parse::().unwrap(); assert_eq!( parsed_desc.get_single_line_value("NAME").unwrap(), @@ -900,12 +900,12 @@ pkgtype=pkg assert_eq!(parsed_desc.base().unwrap(), "filesystem"); // This is the builddate at the time of writing the test. // Package will probably be newer if the fixture contents are regenerated at a later point in time - assert!(parsed_desc.builddate().unwrap() >= 1760286101); + assert!(parsed_desc.builddate().unwrap() >= 1_760_286_101); } #[test] fn test_parse_files() { - const FILES_CONTENT: &str = r#"%FILES% + const FILES_CONTENT: &str = r"%FILES% etc/ etc/protocols etc/services @@ -921,7 +921,7 @@ usr/share/licenses/iana-etc/LICENSE %BACKUP% etc/protocols b9833a5373ef2f5df416f4f71ccb42eb etc/services b80b33810d79289b09bac307a99b4b54 -"#; +"; const EXPECTED_PATHS: &[&str] = &[ "etc/", "etc/protocols", @@ -937,16 +937,13 @@ etc/services b80b33810d79289b09bac307a99b4b54 ]; let mut expected_paths_set = EXPECTED_PATHS .iter() - .map(|path| Utf8Path::new(path)) + .map(Utf8Path::new) .collect::>(); let parsed_files = FILES_CONTENT.parse::().unwrap(); // Test that the generic parser can parse other sections, such as BACKUP - let mut other_section = parsed_files - .get_multi_line_value("BACKUP") - .unwrap() - .into_iter(); + let mut other_section = parsed_files.get_multi_line_value("BACKUP").unwrap().iter(); assert_eq!( other_section.next().unwrap(), "etc/protocols\tb9833a5373ef2f5df416f4f71ccb42eb" @@ -958,7 +955,7 @@ etc/services b80b33810d79289b09bac307a99b4b54 assert_eq!(other_section.next(), None); let parsed_files = LocalAlpmDbFilesFile(parsed_files); - for path_from_files in parsed_files.files().into_iter() { + for path_from_files in parsed_files.files() { assert!( expected_paths_set.remove(path_from_files), "path from files must be expected" @@ -1026,14 +1023,14 @@ DBPathInvalid = /invalid #[test] fn pacman_conf_last_with_inline_comment() { /// A pacman.conf file that is valid, but contains comments in the same line - pub const PACMAN_CONF_MULTIPLE_WITH_INLINE_COMMENT_AND_RELATIVE: &str = r#" + pub const PACMAN_CONF_MULTIPLE_WITH_INLINE_COMMENT_AND_RELATIVE: &str = r" [options] DBPath = /invalid # This is an inline comment [invalid] invalid_option = false [options] DBPath = var/lib/pacman # Last path should win. Test relative paths are working as well. - "#; + "; let dbpath = AlpmComponentsRepo::parse_pacman_conf_dbpath( PACMAN_CONF_MULTIPLE_WITH_INLINE_COMMENT_AND_RELATIVE, ); diff --git a/src/components/bigfiles.rs b/src/components/bigfiles.rs index 3c76e5b..4d83432 100644 --- a/src/components/bigfiles.rs +++ b/src/components/bigfiles.rs @@ -70,7 +70,7 @@ impl BigfilesRepo { let filename = path .file_name() - .map(|s| s.to_string()) + .map(str::to_string) .expect("filename has no basename"); // derive component name from filename diff --git a/src/components/mod.rs b/src/components/mod.rs index 5e4c3ba..7413cf9 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -78,7 +78,7 @@ impl FileType { /// Try to convert from cap_std file type. /// /// Returns `None` for unsupported types (sockets, FIFOs, block/char devices). - pub fn from_cap_std(file_type: &CapFileType) -> Option { + pub fn from_cap_std(file_type: CapFileType) -> Option { if file_type.is_dir() { Some(FileType::Directory) } else if file_type.is_file() { @@ -253,7 +253,7 @@ impl ComponentsRepos { } /// Whether to use strong or weak claims in a claiming pass. -#[derive(Debug)] +#[derive(Debug, Clone, Copy)] enum ClaimStrength { Strong, Weak, @@ -426,7 +426,7 @@ mod tests { .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_secs(); - let rpm_repo = rpm::RpmRepo::load_from_packages(packages, now).unwrap(); + let rpm_repo = rpm::RpmRepo::load_from_packages(packages, now); let repos: Vec> = vec![Box::new(rpm_repo), Box::new(xattr_repo)]; let loaded = ComponentsRepos { diff --git a/src/components/rpm.rs b/src/components/rpm.rs index 1a3ab6f..db9d146 100644 --- a/src/components/rpm.rs +++ b/src/components/rpm.rs @@ -56,12 +56,12 @@ impl RpmRepo { canonicalize_package_paths(rootfs, files, &mut packages) .context("canonicalizing package paths")?; - let mut repo = Self::load_from_packages(packages, now)?; + let mut repo = Self::load_from_packages(packages, now); build_orphan_digest_index(&mut repo, files); Ok(Some(repo)) } - pub fn load_from_packages(packages: rpm_qa::Packages, now: u64) -> Result { + pub fn load_from_packages(packages: rpm_qa::Packages, now: u64) -> Self { let mut components: IndexMap = IndexMap::new(); let mut path_to_components: HashMap> = HashMap::new(); @@ -118,7 +118,7 @@ impl RpmRepo { None => false, }; - for (path, mut file_info) in pkg.files.into_iter() { + for (path, mut file_info) in pkg.files { if !is_sha256 { file_info.digest = None; } @@ -146,12 +146,12 @@ impl RpmRepo { "loaded rpm database" ); - Ok(Self { + Self { components, path_to_components, orphan_digest_index: HashMap::new(), orphan_sizes: HashSet::new(), - }) + } } } @@ -375,7 +375,7 @@ fn parse_srpm_name(srpm: &str) -> &str { } fn file_info_to_file_type(fi: &FileInfo) -> Option { - let file_type = (fi.mode as libc::mode_t) & libc::S_IFMT; + let file_type = libc::mode_t::from(fi.mode) & libc::S_IFMT; match file_type { libc::S_IFDIR => Some(FileType::Directory), libc::S_IFREG => Some(FileType::File), @@ -434,7 +434,7 @@ mod tests { #[test] fn test_claims_for_path() { let packages = rpm_qa::load_from_str(FIXTURE).unwrap(); - let repo = RpmRepo::load_from_packages(packages, now_secs()).unwrap(); + let repo = RpmRepo::load_from_packages(packages, now_secs()); // /usr/bin/bash is a file owned by bash let claims = @@ -442,7 +442,7 @@ mod tests { assert_eq!(claims.len(), 1); let info = repo.component_info(claims[0]); assert_eq!(info.name, "bash"); - assert_eq!(info.mtime_clamp, 1753299195); + assert_eq!(info.mtime_clamp, 1_753_299_195); // /usr/bin/sh is a symlink owned by bash let claims = @@ -457,7 +457,7 @@ mod tests { assert_eq!(claims.len(), 1); let info = repo.component_info(claims[0]); assert_eq!(info.name, "glibc"); - assert_eq!(info.mtime_clamp, 1771428496); + assert_eq!(info.mtime_clamp, 1_771_428_496); // Unowned file should not be claimed let claims = @@ -483,7 +483,7 @@ mod tests { #[test] fn test_claims_for_path_wrong_type() { let packages = rpm_qa::load_from_str(FIXTURE).unwrap(); - let repo = RpmRepo::load_from_packages(packages, now_secs()).unwrap(); + let repo = RpmRepo::load_from_packages(packages, now_secs()); // /usr/bin/bash is a file in RPM, but we query as symlink let claims = @@ -498,7 +498,7 @@ mod tests { #[test] fn test_shared_directories_claimed_by_multiple_components() { let packages = rpm_qa::load_from_str(FIXTURE).unwrap(); - let repo = RpmRepo::load_from_packages(packages, now_secs()).unwrap(); + let repo = RpmRepo::load_from_packages(packages, now_secs()); // /usr/lib/.build-id is a well-known directory shared by many packages let claims = repo.strong_claims_for_path( @@ -669,18 +669,18 @@ mod tests { arch: "x86_64".into(), license: "MIT".into(), size: 1000, - buildtime: now - 200000, + buildtime: now - 200_000, installtime: now, sourcerpm: Some(srpm.into()), digest_algo: None, - changelog_times: vec![now - 200000, now - 300000], + changelog_times: vec![now - 200_000, now - 300_0000], files: BTreeMap::new(), }; // "foo2" has fresher changelogs so should have lower stability let mut foo2 = foo.clone(); foo2.name = "foo2".into(); - foo2.changelog_times = vec![now, now - 100000]; + foo2.changelog_times = vec![now, now - 100_000]; let stab_foo = calculate_stability(&foo.changelog_times, foo.buildtime, now); let stab_foo2 = calculate_stability(&foo2.changelog_times, foo2.buildtime, now); @@ -688,9 +688,9 @@ mod tests { let assert_stability = |first: &Package, second: &Package| { let mut packages: rpm_qa::Packages = HashMap::new(); - packages.insert(first.name.to_string(), first.clone()); - packages.insert(second.name.to_string(), second.clone()); - let repo = RpmRepo::load_from_packages(packages, now).unwrap(); + packages.insert(first.name.clone(), first.clone()); + packages.insert(second.name.clone(), second.clone()); + let repo = RpmRepo::load_from_packages(packages, now); let info = repo.component_info(ComponentId(0)); assert_eq!(info.name, "foo"); // the component should use the min (most pessimistic) stability @@ -744,7 +744,7 @@ mod tests { bash_fi.digest = Some(digest); bash_fi.size = size; - let mut repo = RpmRepo::load_from_packages(packages, now_secs()).unwrap(); + let mut repo = RpmRepo::load_from_packages(packages, now_secs()); build_orphan_digest_index(&mut repo, &files); // the moved file should be reclaimed by bash's SRPM @@ -786,7 +786,7 @@ mod tests { glibc_fi.digest = Some(digest); glibc_fi.size = size; - let mut repo = RpmRepo::load_from_packages(packages, now_secs()).unwrap(); + let mut repo = RpmRepo::load_from_packages(packages, now_secs()); build_orphan_digest_index(&mut repo, &files); // ambiguous digest should not claim anything diff --git a/src/packing.rs b/src/packing.rs index d35177d..d9d98a5 100644 --- a/src/packing.rs +++ b/src/packing.rs @@ -148,7 +148,7 @@ fn isolate_size_outliers(items: &[PackItem], budget: usize) -> (Vec, raw_mad }; - let high_size_limit = median + SIZE_OUTLIER_THRESHOLD * mad; + let high_size_limit = SIZE_OUTLIER_THRESHOLD.mul_add(mad, median); tracing::debug!(median, mad, high_size_limit, "phase 1: size thresholds"); @@ -171,7 +171,7 @@ fn isolate_size_outliers(items: &[PackItem], budget: usize) -> (Vec, // Cap high-size singletons at a fraction of the budget. // Excess are pushed into the remaining pool. - let reserved_bins = if remaining_indices.is_empty() { 0 } else { 1 }; + let reserved_bins = usize::from(!remaining_indices.is_empty()); let hs_bins_limit = ((budget.saturating_sub(reserved_bins) as f64) * HIGH_SIZE_CAP).floor() as usize; let hs_bins = high_size_indices.len().min(hs_bins_limit); @@ -398,7 +398,7 @@ fn compute_median(values: &[f64]) -> f64 { sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal)); let mid = sorted.len() / 2; if sorted.len().is_multiple_of(2) { - (sorted[mid - 1] + sorted[mid]) / 2.0 + f64::midpoint(sorted[mid - 1], sorted[mid]) } else { sorted[mid] } @@ -453,7 +453,7 @@ mod tests { // check all indices present exactly once (no loss, no duplication) let mut output_indices: Vec = result.iter().flat_map(|g| &g.indices).copied().collect(); - output_indices.sort(); + output_indices.sort_unstable(); let expected_indices: Vec = (0..input.len()).collect(); assert_eq!(output_indices, expected_indices, "indices mismatch"); @@ -525,7 +525,7 @@ mod tests { fn test_large_components_get_singletons() { // One very large component and several small ones let items = vec![ - make_item("huge", 100000, 0.9), + make_item("huge", 100_000, 0.9), make_item("small1", 10, 0.9), make_item("small2", 10, 0.9), make_item("small3", 10, 0.9), @@ -576,7 +576,7 @@ mod tests { // for 9 items whose stabilities span 3 tiers. Previously this dropped // items from tiers that got 0 bins. let items = vec![ - make_item("huge", 100000, 0.5), + make_item("huge", 100_000, 0.5), make_item("a", 100, 0.99), make_item("b", 100, 0.98), make_item("c", 100, 0.97), diff --git a/src/scan.rs b/src/scan.rs index d16727e..e47b382 100644 --- a/src/scan.rs +++ b/src/scan.rs @@ -78,15 +78,15 @@ impl<'a> Scanner<'a> { .with_context(|| format!("getting metadata for {}", path))?; // Check file type early, before reading xattrs - let file_type = match FileType::from_cap_std(&metadata.file_type()) { + let file_type = match FileType::from_cap_std(metadata.file_type()) { Some(ft) => ft, None => { if self.skip_special_files { tracing::debug!(path = %path, "skipping special file"); return Ok(ControlFlow::Continue(())); - } else { - anyhow::bail!("special file type not supported: {}", path); } + + anyhow::bail!("special file type not supported: {}", path); } }; @@ -333,7 +333,7 @@ mod tests { assert_eq!(get_file_type(&files, "/regular.txt"), Some(FileType::File)); // Socket should be skipped (not in the map) - assert!(files.get(Utf8Path::new("/test.sock")).is_none()); + assert!(!files.contains_key(Utf8Path::new("/test.sock"))); } #[test] diff --git a/src/tar.rs b/src/tar.rs index 1aa7edb..6dca509 100644 --- a/src/tar.rs +++ b/src/tar.rs @@ -10,6 +10,7 @@ use ocidir::oci_spec::image as oci_image; use crate::components::{FileInfo, FileMap, FileType}; /// Compression options for OCI archives. +#[derive(Debug, Clone, Copy)] pub enum ArchiveCompression { /// No compression. None, @@ -23,7 +24,7 @@ pub enum LayerWriter<'a> { Gzip(ocidir::LayerWriter<'a, flate2::write::GzEncoder>>), } -impl<'a> Write for LayerWriter<'a> { +impl Write for LayerWriter<'_> { fn write(&mut self, buf: &[u8]) -> std::io::Result { match self { LayerWriter::Uncompressed(w) => w.write(buf), @@ -39,7 +40,7 @@ impl<'a> Write for LayerWriter<'a> { } } -impl<'a> LayerWriter<'a> { +impl LayerWriter<'_> { /// Complete the layer and return the layer. pub fn complete(self) -> Result { match self { @@ -187,8 +188,8 @@ fn strip_root_prefix(path: &Utf8Path) -> &Utf8Path { fn write_header_from_file_info(header: &mut tar::Header, file_info: &FileInfo, mtime_clamp: u64) { let mtime = std::cmp::min(file_info.mtime, mtime_clamp); header.set_mtime(mtime); - header.set_uid(file_info.uid as u64); - header.set_gid(file_info.gid as u64); + header.set_uid(u64::from(file_info.uid)); + header.set_gid(u64::from(file_info.gid)); header.set_mode(file_info.mode); } diff --git a/src/utils.rs b/src/utils.rs index 7d1061b..90a6955 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -96,7 +96,7 @@ pub fn calculate_stability(changelog_times: &[u64], buildtime: u64, now: u64) -> let num_relevant_changes = if changelog_times.is_empty() { // If there are no changelog entries, use the buildtime as a single data point - if buildtime >= lookback_start { 1 } else { 0 } + i32::from(buildtime >= lookback_start) } else { // Count only entries within the lookback window, and bin by days. The // reason for binning by days is that multiple package releases within a @@ -136,7 +136,7 @@ pub fn calculate_stability(changelog_times: &[u64], buildtime: u64, now: u64) -> } // lambda in our case is changes per day - let lambda = num_relevant_changes as f64 / span_days; + let lambda = f64::from(num_relevant_changes) / span_days; (-lambda * STABILITY_PERIOD_DAYS).exp() } @@ -214,11 +214,10 @@ fn canonicalize_dir_path( let is_symlink = files .get(¤t_path) - .map(|fi| fi.file_type == FileType::Symlink) // Technically if we fallback here it means it doesn't even exist in the // rootfs so it won't even be claimed. But it feels overkill to try to // e.g. return an Option and handle that everywhere. - .unwrap_or(false); + .is_some_and(|fi| fi.file_type == FileType::Symlink); let canonical = if is_symlink { let rel_path = current_path @@ -305,7 +304,7 @@ mod tests { fn test_parse_rfc3339_epoch() { assert_eq!( parse_rfc3339_epoch("2023-11-14T22:13:20Z").unwrap(), - 1700000000 + 1_700_000_000 ); assert_eq!(parse_rfc3339_epoch("1970-01-01T00:00:00Z").unwrap(), 0); assert!(parse_rfc3339_epoch("not-a-date").is_err()); @@ -319,9 +318,9 @@ mod tests { assert_eq!(format_size(1023), "1023 B"); assert_eq!(format_size(1024), "1.0 KiB"); assert_eq!(format_size(1536), "1.5 KiB"); - assert_eq!(format_size(1048576), "1.0 MiB"); - assert_eq!(format_size(1073741824), "1.0 GiB"); - assert_eq!(format_size(1610612736), "1.5 GiB"); + assert_eq!(format_size(1_048_576), "1.0 MiB"); + assert_eq!(format_size(1_073_741_824), "1.0 GiB"); + assert_eq!(format_size(1_610_612_736), "1.5 GiB"); } #[test]