From d02e44f82dea8095d53293100944b0361ad2110f Mon Sep 17 00:00:00 2001 From: Anthony Vargas <90121982+Speedrunyourknowledge@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:44:02 -0500 Subject: [PATCH 1/5] Add --summary flag to cli output --- rust/cli/src/main.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/rust/cli/src/main.rs b/rust/cli/src/main.rs index 6f5c64bc..9d463c81 100644 --- a/rust/cli/src/main.rs +++ b/rust/cli/src/main.rs @@ -48,6 +48,10 @@ struct Flags { #[arg(long)] no_dereference: bool, + /// Prints a summary of file types at the end of the output. + #[arg(long)] + summary: bool, + #[clap(flatten)] colors: Colors, @@ -211,12 +215,26 @@ async fn main() -> Result<()> { if flags.format.json { print!("["); } + // Initialize counter for file types + let mut type_counts: HashMap = HashMap::new(); + // Reorder responses to match input order let mut reorder = Reorder::default(); let mut errors = false; while let Some(response) = result_receiver.recv().await { reorder.push(response?); while let Some(response) = reorder.pop() { errors |= response.result.is_err(); + // Count file type for summary + if flags.summary { + // If result is ok, extract the description (e.g., "Python source") + if let Ok(file_type) = &response.result { + // Can change .description to .label for shorter names like "python" + let label = file_type.info().description.to_string(); + // If the label exists, add 1. If not, insert 0 then add 1. + *type_counts.entry(label).or_insert(0) += 1; + } + } + // Print output if flags.format.json { if reorder.next != 1 { print!(","); @@ -236,6 +254,16 @@ async fn main() -> Result<()> { } println!("]"); } + // Print summary if requested + if flags.summary && !flags.format.json && !flags.format.jsonl { + println!("--- Summary ---"); + // Sort the results by count (descending) + let mut sorted_counts: Vec<_> = type_counts.into_iter().collect(); + sorted_counts.sort_by(|a, b| b.1.cmp(&a.1)); + for (label, count) in sorted_counts { + println!("{}: {}", label, count); + } + } if errors { std::process::exit(1); } From d735809723f90007f8c67fcda21fd4caa9a7dcf0 Mon Sep 17 00:00:00 2001 From: Anthony Vargas <90121982+Speedrunyourknowledge@users.noreply.github.com> Date: Wed, 26 Nov 2025 17:05:25 -0500 Subject: [PATCH 2/5] Update summary to only display if no errors --- rust/cli/src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rust/cli/src/main.rs b/rust/cli/src/main.rs index 9d463c81..335bdeef 100644 --- a/rust/cli/src/main.rs +++ b/rust/cli/src/main.rs @@ -254,7 +254,10 @@ async fn main() -> Result<()> { } println!("]"); } - // Print summary if requested + if errors { + std::process::exit(1); + } + // Print summary if requested, only if there were no errors if flags.summary && !flags.format.json && !flags.format.jsonl { println!("--- Summary ---"); // Sort the results by count (descending) @@ -264,9 +267,6 @@ async fn main() -> Result<()> { println!("{}: {}", label, count); } } - if errors { - std::process::exit(1); - } Ok(()) } From 871e8fac1953fc365d1a148caabc0565593a3a40 Mon Sep 17 00:00:00 2001 From: Anthony Vargas <90121982+Speedrunyourknowledge@users.noreply.github.com> Date: Sat, 29 Nov 2025 06:09:31 -0500 Subject: [PATCH 3/5] Add color and alphabetical sorting to summary --- rust/cli/src/main.rs | 55 +++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 16 deletions(-) diff --git a/rust/cli/src/main.rs b/rust/cli/src/main.rs index 335bdeef..09481b72 100644 --- a/rust/cli/src/main.rs +++ b/rust/cli/src/main.rs @@ -22,6 +22,7 @@ use std::sync::Arc; use anyhow::{bail, ensure, Result}; use clap::{Args, Parser}; use colored::ColoredString; +use colored::Colorize; use magika_lib::{ self as magika, ContentType, Features, FeaturesOrRuled, FileType, InferredType, OverwriteReason, Session, TypeInfo, @@ -215,26 +216,26 @@ async fn main() -> Result<()> { if flags.format.json { print!("["); } - // Initialize counter for file types - let mut type_counts: HashMap = HashMap::new(); - // Reorder responses to match input order + // Initializes counter for file types. + let mut type_counts: HashMap<(String, String), usize> = HashMap::new(); let mut reorder = Reorder::default(); let mut errors = false; + // Prints results, reordering as needed to match input order. while let Some(response) = result_receiver.recv().await { reorder.push(response?); while let Some(response) = reorder.pop() { errors |= response.result.is_err(); - // Count file type for summary + // Counts file type for the final summary. if flags.summary { - // If result is ok, extract the description (e.g., "Python source") + // If result is ok, extracts the description (e.g., "Python source"). if let Ok(file_type) = &response.result { - // Can change .description to .label for shorter names like "python" - let label = file_type.info().description.to_string(); - // If the label exists, add 1. If not, insert 0 then add 1. - *type_counts.entry(label).or_insert(0) += 1; + let type_label = file_type.info().description.to_string(); + let group = file_type.info().group.to_string(); + // Increments the count in the HashMap, inserting 0 if the key does not exist. + *type_counts.entry((type_label, group)).or_insert(0) += 1; } } - // Print output + // Prints output. if flags.format.json { if reorder.next != 1 { print!(","); @@ -257,14 +258,23 @@ async fn main() -> Result<()> { if errors { std::process::exit(1); } - // Print summary if requested, only if there were no errors + // Prints summary if requested (only if there were no errors). if flags.summary && !flags.format.json && !flags.format.jsonl { println!("--- Summary ---"); - // Sort the results by count (descending) + // Sorts by count (descending). let mut sorted_counts: Vec<_> = type_counts.into_iter().collect(); - sorted_counts.sort_by(|a, b| b.1.cmp(&a.1)); - for (label, count) in sorted_counts { - println!("{}: {}", label, count); + sorted_counts.sort_by(|a, b| { + let count_cmp = b.1.cmp(&a.1); + if count_cmp == std::cmp::Ordering::Equal { + // Sorts alphabetically (case-insensitive). + // a.0 .0 accesses the type_label. + a.0 .0.to_lowercase().cmp(&b.0 .0.to_lowercase()) + } else { + count_cmp + } + }); + for ((type_label, group), count) in sorted_counts { + println!("{}: {}", color_type_label(&type_label, &group), count); } } Ok(()) @@ -582,7 +592,6 @@ impl Response { } fn color(&self, result: ColoredString) -> ColoredString { - use colored::Colorize as _; // We only use true colors (except for errors). If the terminal doesn't support true colors, // the colored crate will automatically choose the closest one. match &self.result { @@ -615,3 +624,17 @@ fn join>(xs: impl IntoIterator) -> String { result.push(']'); result } + +fn color_type_label(type_label: &str, group: &str) -> colored::ColoredString { + match group { + "application" => type_label.truecolor(0xf4, 0x3f, 0x5e), + "archive" => type_label.truecolor(0xf5, 0x9e, 0x0b), + "audio" => type_label.truecolor(0x84, 0xcc, 0x16), + "code" => type_label.truecolor(0x8b, 0x5c, 0xf6), + "document" => type_label.truecolor(0x3b, 0x82, 0xf6), + "executable" => type_label.truecolor(0xec, 0x48, 0x99), + "image" => type_label.truecolor(0x06, 0xb6, 0xd4), + "video" => type_label.truecolor(0x10, 0xb9, 0x81), + _ => type_label.bold().truecolor(0xcc, 0xcc, 0xcc), + } +} From a5c0ab045788a49c7cbc89f330d08b8a77d363dd Mon Sep 17 00:00:00 2001 From: Anthony Vargas <90121982+Speedrunyourknowledge@users.noreply.github.com> Date: Mon, 1 Dec 2025 04:01:39 -0500 Subject: [PATCH 4/5] Fix format for nightly rust --- rust/cli/src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rust/cli/src/main.rs b/rust/cli/src/main.rs index 09481b72..fdb1a9aa 100644 --- a/rust/cli/src/main.rs +++ b/rust/cli/src/main.rs @@ -21,8 +21,7 @@ use std::sync::Arc; use anyhow::{bail, ensure, Result}; use clap::{Args, Parser}; -use colored::ColoredString; -use colored::Colorize; +use colored::{ColoredString, Colorize}; use magika_lib::{ self as magika, ContentType, Features, FeaturesOrRuled, FileType, InferredType, OverwriteReason, Session, TypeInfo, From 11eef79234fa6808a6ca5bea48d7c108818060fd Mon Sep 17 00:00:00 2001 From: Anthony Vargas <90121982+Speedrunyourknowledge@users.noreply.github.com> Date: Mon, 1 Dec 2025 04:15:12 -0500 Subject: [PATCH 5/5] Sync generated rust file to fix CI error --- rust/cli/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/rust/cli/README.md b/rust/cli/README.md index ebaf28e7..f0dcb3d1 100644 --- a/rust/cli/README.md +++ b/rust/cli/README.md @@ -142,6 +142,9 @@ Options: --no-dereference Identifies symbolic links as is instead of identifying their content by following them + --summary + Prints a summary of file types at the end of the output + --colors Prints with colors regardless of terminal support