mirror of
https://github.com/RGBCube/dix
synced 2025-07-28 12:17:45 +00:00
main: separate logging channels via log
crate
This commit is contained in:
parent
dde5723cee
commit
f52281d691
3 changed files with 266 additions and 59 deletions
90
Cargo.lock
generated
90
Cargo.lock
generated
|
@ -107,6 +107,29 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.11.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"env_filter",
|
||||
"jiff",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
|
@ -119,6 +142,36 @@ version = "1.70.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
|
||||
|
||||
[[package]]
|
||||
name = "jiff"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d07d8d955d798e7a4d6f9c58cd1f1916e790b42b092758a9ef6e16fef9f1b3fd"
|
||||
dependencies = [
|
||||
"jiff-static",
|
||||
"log",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jiff-static"
|
||||
version = "0.2.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f244cfe006d98d26f859c7abd1318d85327e1882dc9cef80f62daeeb0adcf300"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
|
@ -130,6 +183,8 @@ name = "nnpdt"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"env_logger",
|
||||
"log",
|
||||
"regex",
|
||||
"thiserror",
|
||||
"yansi",
|
||||
|
@ -141,6 +196,21 @@ version = "1.21.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "350e9b48cbc6b0e028b0473b114454c6316e57336ee184ceab6e53f72c178b3e"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic-util"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.95"
|
||||
|
@ -188,6 +258,26 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.219"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
|
|
@ -6,5 +6,7 @@ edition = "2024"
|
|||
[dependencies]
|
||||
clap = { version = "4.5.37", features = ["derive"] }
|
||||
regex = "1.11.1"
|
||||
thiserror = "2.0.12"
|
||||
yansi = "1.0.1"
|
||||
thiserror = "2.0.12"
|
||||
log = "0.4.20"
|
||||
env_logger = "0.11.3"
|
||||
|
|
231
src/main.rs
231
src/main.rs
|
@ -1,10 +1,12 @@
|
|||
use clap::Parser;
|
||||
use core::str;
|
||||
use env_logger;
|
||||
use log::{debug, error, info, warn};
|
||||
use regex::Regex;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
process::Command,
|
||||
string::String,
|
||||
string::{String, ToString},
|
||||
sync::OnceLock,
|
||||
thread,
|
||||
};
|
||||
|
@ -49,6 +51,14 @@ struct Args {
|
|||
/// Print the closure size
|
||||
#[arg(long, short)]
|
||||
closure_size: bool,
|
||||
|
||||
/// Verbosity level: -v for debug, -vv for trace
|
||||
#[arg(short, long, action = clap::ArgAction::Count)]
|
||||
verbose: u8,
|
||||
|
||||
/// Silence all output except errors
|
||||
#[arg(short, long)]
|
||||
quiet: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -78,14 +88,35 @@ impl<'a> Package<'a> {
|
|||
fn main() {
|
||||
let args = Args::parse();
|
||||
|
||||
println!("Nix available: {}", check_nix_available());
|
||||
// Configure logger based on verbosity flags and environment variables
|
||||
// Respects RUST_LOG environment variable if present.
|
||||
// XXX:We can also dedicate a specific env variable for this tool, if we want to.
|
||||
let env = env_logger::Env::default().filter_or(
|
||||
"RUST_LOG",
|
||||
if args.quiet {
|
||||
"error"
|
||||
} else {
|
||||
match args.verbose {
|
||||
0 => "info",
|
||||
1 => "debug",
|
||||
_ => "trace",
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Build and initialize the logger
|
||||
env_logger::Builder::from_env(env)
|
||||
.format_timestamp(Some(env_logger::fmt::TimestampPrecision::Seconds))
|
||||
.init();
|
||||
|
||||
debug!("Nix available: {}", check_nix_available()); // XXX: is this supposed to be user-facing?
|
||||
println!("<<< {}", args.path.to_string_lossy());
|
||||
println!(">>> {}", args.path2.to_string_lossy());
|
||||
|
||||
// Handles to the threads collecting closure size information
|
||||
// handles to the threads collecting closure size information
|
||||
// We do this as early as possible because nix is slow.
|
||||
let closure_size_handles = if args.closure_size {
|
||||
debug!("Calculating closure sizes in background");
|
||||
let path = args.path.clone();
|
||||
let path2 = args.path2.clone();
|
||||
Some((
|
||||
|
@ -98,17 +129,41 @@ fn main() {
|
|||
|
||||
// Get package lists and handle potential errors
|
||||
let package_list_pre = match get_packages(&args.path) {
|
||||
Ok(packages) => packages,
|
||||
Ok(packages) => {
|
||||
debug!("Found {} packages in first closure", packages.len());
|
||||
packages
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error getting packages from path {}: {}", args.path.display(), e);
|
||||
error!(
|
||||
"Error getting packages from path {}: {}",
|
||||
args.path.display(),
|
||||
e
|
||||
);
|
||||
eprintln!(
|
||||
"Error getting packages from path {}: {}",
|
||||
args.path.display(),
|
||||
e
|
||||
);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
||||
let package_list_post = match get_packages(&args.path2) {
|
||||
Ok(packages) => packages,
|
||||
Ok(packages) => {
|
||||
debug!("Found {} packages in second closure", packages.len());
|
||||
packages
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Error getting packages from path {}: {}", args.path2.display(), e);
|
||||
error!(
|
||||
"Error getting packages from path {}: {}",
|
||||
args.path2.display(),
|
||||
e
|
||||
);
|
||||
eprintln!(
|
||||
"Error getting packages from path {}: {}",
|
||||
args.path2.display(),
|
||||
e
|
||||
);
|
||||
Vec::new()
|
||||
}
|
||||
};
|
||||
|
@ -121,11 +176,9 @@ fn main() {
|
|||
match get_version(&**p) {
|
||||
Ok((name, version)) => {
|
||||
pre.entry(name).or_default().insert(version);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
if cfg!(debug_assertions) {
|
||||
eprintln!("Error parsing package version: {e}");
|
||||
}
|
||||
debug!("Error parsing package version: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -134,11 +187,9 @@ fn main() {
|
|||
match get_version(&**p) {
|
||||
Ok((name, version)) => {
|
||||
post.entry(name).or_default().insert(version);
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
if cfg!(debug_assertions) {
|
||||
eprintln!("Error parsing package version: {e}");
|
||||
}
|
||||
debug!("Error parsing package version: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +204,20 @@ fn main() {
|
|||
// Get the intersection of the package names for version changes
|
||||
let changed: HashSet<&str> = &pre_keys & &post_keys;
|
||||
|
||||
debug!("Added packages: {}", added.len());
|
||||
debug!("Removed packages: {}", removed.len());
|
||||
debug!(
|
||||
"Changed packages: {}",
|
||||
changed
|
||||
.iter()
|
||||
.filter(|p| !p.is_empty())
|
||||
.filter_map(|p| match (pre.get(p), post.get(p)) {
|
||||
(Some(ver_pre), Some(ver_post)) if ver_pre != ver_post => Some(p),
|
||||
_ => None,
|
||||
})
|
||||
.count()
|
||||
);
|
||||
|
||||
println!("Difference between the two generations:");
|
||||
println!();
|
||||
|
||||
|
@ -165,15 +230,20 @@ fn main() {
|
|||
if let Some((pre_handle, post_handle)) = closure_size_handles {
|
||||
match (pre_handle.join(), post_handle.join()) {
|
||||
(Ok(Ok(pre_size)), Ok(Ok(post_size))) => {
|
||||
debug!("Pre closure size: {} MiB", pre_size);
|
||||
debug!("Post closure size: {} MiB", post_size);
|
||||
|
||||
println!("{}", "Closure Size:".underline().bold());
|
||||
println!("Before: {pre_size} MiB");
|
||||
println!("After: {post_size} MiB");
|
||||
println!("Difference: {} MiB", post_size - pre_size);
|
||||
}
|
||||
(Ok(Err(e)), _) | (_, Ok(Err(e))) => {
|
||||
error!("Error getting closure size: {}", e);
|
||||
eprintln!("Error getting closure size: {e}");
|
||||
}
|
||||
_ => {
|
||||
error!("Failed to get closure size information due to a thread error");
|
||||
eprintln!("Error: Failed to get closure size information due to a thread error");
|
||||
}
|
||||
}
|
||||
|
@ -182,6 +252,8 @@ fn main() {
|
|||
|
||||
/// Gets the packages in a closure
|
||||
fn get_packages(path: &std::path::Path) -> Result<Vec<String>> {
|
||||
debug!("Getting packages from path: {}", path.display());
|
||||
|
||||
// Get the nix store paths using `nix-store --query --references <path>``
|
||||
let output = Command::new("nix-store")
|
||||
.arg("--query")
|
||||
|
@ -191,18 +263,23 @@ fn get_packages(path: &std::path::Path) -> Result<Vec<String>> {
|
|||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("nix-store command failed: {}", stderr);
|
||||
return Err(AppError::CommandFailed(format!(
|
||||
"nix-store command failed: {stderr}"
|
||||
)));
|
||||
}
|
||||
|
||||
let list = str::from_utf8(&output.stdout)?;
|
||||
Ok(list.lines().map(str::to_owned).collect())
|
||||
let packages: Vec<String> = list.lines().map(str::to_owned).collect();
|
||||
debug!("Found {} packages", packages.len());
|
||||
Ok(packages)
|
||||
}
|
||||
|
||||
/// Gets the dependencies of the packages in a closure
|
||||
fn get_dependencies(path: &std::path::Path) -> Result<Vec<String>> {
|
||||
// Get the nix store paths using `nix-store --query --requisites <path>``
|
||||
debug!("Getting dependencies from path: {}", path.display());
|
||||
|
||||
// Get the nix store paths using `nix-store --query --requisites <path>`
|
||||
let output = Command::new("nix-store")
|
||||
.arg("--query")
|
||||
.arg("--requisites")
|
||||
|
@ -211,13 +288,16 @@ fn get_dependencies(path: &std::path::Path) -> Result<Vec<String>> {
|
|||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("nix-store command failed: {}", stderr);
|
||||
return Err(AppError::CommandFailed(format!(
|
||||
"nix-store command failed: {stderr}"
|
||||
)));
|
||||
}
|
||||
|
||||
let list = str::from_utf8(&output.stdout)?;
|
||||
Ok(list.lines().map(str::to_owned).collect())
|
||||
let dependencies: Vec<String> = list.lines().map(str::to_owned).collect();
|
||||
debug!("Found {} dependencies", dependencies.len());
|
||||
Ok(dependencies)
|
||||
}
|
||||
|
||||
// Returns a reference to the compiled regex pattern.
|
||||
|
@ -256,13 +336,21 @@ fn get_version<'a>(pack: impl Into<&'a str>) -> Result<(&'a str, &'a str)> {
|
|||
|
||||
fn check_nix_available() -> bool {
|
||||
// Check if nix is available on the host system.
|
||||
debug!("Checking nix command availability");
|
||||
let nix_available = Command::new("nix").arg("--version").output().ok();
|
||||
let nix_query_available = Command::new("nix-store").arg("--version").output().ok();
|
||||
|
||||
nix_available.is_some() && nix_query_available.is_some()
|
||||
let result = nix_available.is_some() && nix_query_available.is_some();
|
||||
if !result {
|
||||
warn!("Nix commands not available, functionality may be limited");
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn get_closure_size(path: &std::path::Path) -> Result<i64> {
|
||||
debug!("Calculating closure size for path: {}", path.display());
|
||||
|
||||
// Run nix path-info command to get closure size
|
||||
let output = Command::new("nix")
|
||||
.arg("path-info")
|
||||
|
@ -272,6 +360,7 @@ fn get_closure_size(path: &std::path::Path) -> Result<i64> {
|
|||
|
||||
if !output.status.success() {
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
error!("nix path-info command failed: {}", stderr);
|
||||
return Err(AppError::CommandFailed(format!(
|
||||
"nix path-info command failed: {stderr}"
|
||||
)));
|
||||
|
@ -280,20 +369,33 @@ fn get_closure_size(path: &std::path::Path) -> Result<i64> {
|
|||
let stdout = str::from_utf8(&output.stdout)?;
|
||||
|
||||
// Parse the last word in the output as an integer (in bytes)
|
||||
stdout
|
||||
let size = stdout
|
||||
.split_whitespace()
|
||||
.last()
|
||||
.ok_or_else(|| AppError::ParseError("Unexpected output format from nix path-info".into()))?
|
||||
.ok_or_else(|| {
|
||||
let err = "Unexpected output format from nix path-info";
|
||||
error!("{}", err);
|
||||
AppError::ParseError(err.into())
|
||||
})?
|
||||
.parse::<i64>()
|
||||
.map_err(|e| AppError::ParseError(format!("Failed to parse size value: {e}")))
|
||||
.map(|size| size / 1024 / 1024) // Convert to MiB
|
||||
.map_err(|e| {
|
||||
let err = format!("Failed to parse size value: {e}");
|
||||
error!("{}", err);
|
||||
AppError::ParseError(err)
|
||||
})?;
|
||||
|
||||
// Convert to MiB
|
||||
let size_mib = size / 1024 / 1024;
|
||||
debug!("Closure size for {}: {} MiB", path.display(), size_mib);
|
||||
Ok(size_mib)
|
||||
}
|
||||
|
||||
fn print_added(set: HashSet<&str>, post: &HashMap<&str, HashSet<&str>>) {
|
||||
println!("{}", "Packages added:".underline().bold());
|
||||
|
||||
// Use sorted output
|
||||
let mut sorted: Vec<_> = set.iter()
|
||||
// Use sorted outpu
|
||||
let mut sorted: Vec<_> = set
|
||||
.iter()
|
||||
.filter_map(|p| post.get(p).map(|ver| (*p, ver)))
|
||||
.collect();
|
||||
|
||||
|
@ -314,18 +416,26 @@ fn print_added(set: HashSet<&str>, post: &HashMap<&str, HashSet<&str>>) {
|
|||
|
||||
fn print_removed(set: HashSet<&str>, pre: &HashMap<&str, HashSet<&str>>) {
|
||||
println!("{}", "Packages removed:".underline().bold());
|
||||
set.iter()
|
||||
|
||||
// Use sorted output for more predictable and readable results
|
||||
let mut sorted: Vec<_> = set
|
||||
.iter()
|
||||
.filter_map(|p| pre.get(p).map(|ver| (*p, ver)))
|
||||
.for_each(|(p, ver)| {
|
||||
let version_str = ver.iter().copied().collect::<Vec<_>>().join(" ");
|
||||
println!(
|
||||
"{} {} {} {}",
|
||||
"[R:]".red().bold(),
|
||||
p,
|
||||
"@".yellow(),
|
||||
version_str.blue()
|
||||
);
|
||||
});
|
||||
.collect();
|
||||
|
||||
// Sort by package name for consistent output
|
||||
sorted.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
|
||||
for (p, ver) in sorted {
|
||||
let version_str = ver.iter().copied().collect::<Vec<_>>().join(" ");
|
||||
println!(
|
||||
"{} {} {} {}",
|
||||
"[R:]".red().bold(),
|
||||
p,
|
||||
"@".yellow(),
|
||||
version_str.blue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn print_changes(
|
||||
|
@ -334,27 +444,32 @@ fn print_changes(
|
|||
post: &HashMap<&str, HashSet<&str>>,
|
||||
) {
|
||||
println!("{}", "Version changes:".underline().bold());
|
||||
set.iter()
|
||||
.filter(|p| !p.is_empty())
|
||||
.filter_map(|p| {
|
||||
match (pre.get(p), post.get(p)) {
|
||||
(Some(ver_pre), Some(ver_post)) if ver_pre != ver_post => {
|
||||
Some((p, ver_pre, ver_post))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
.for_each(|(p, ver_pre, ver_post)| {
|
||||
let version_str_pre = ver_pre.iter().copied().collect::<Vec<_>>().join(" ");
|
||||
let version_str_post = ver_post.iter().copied().collect::<Vec<_>>().join(", ");
|
||||
|
||||
println!(
|
||||
"{} {} {} {} ~> {}",
|
||||
"[C:]".bold().bright_yellow(),
|
||||
p,
|
||||
"@".yellow(),
|
||||
version_str_pre.magenta(),
|
||||
version_str_post.blue()
|
||||
);
|
||||
});
|
||||
// Use sorted output for more predictable and readable results
|
||||
let mut changes = Vec::new();
|
||||
|
||||
for p in set.iter().filter(|p| !p.is_empty()) {
|
||||
if let (Some(ver_pre), Some(ver_post)) = (pre.get(p), post.get(p)) {
|
||||
if ver_pre != ver_post {
|
||||
changes.push((*p, ver_pre, ver_post));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by package name for consistent output
|
||||
changes.sort_by(|(a, _, _), (b, _, _)| a.cmp(b));
|
||||
|
||||
for (p, ver_pre, ver_post) in changes {
|
||||
let version_str_pre = ver_pre.iter().copied().collect::<Vec<_>>().join(" ");
|
||||
let version_str_post = ver_post.iter().copied().collect::<Vec<_>>().join(", ");
|
||||
|
||||
println!(
|
||||
"{} {} {} {} ~> {}",
|
||||
"[C:]".bold().bright_yellow(),
|
||||
p,
|
||||
"@".yellow(),
|
||||
version_str_pre.magenta(),
|
||||
version_str_post.blue()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue