1
Fork 0
mirror of https://github.com/RGBCube/dix synced 2025-07-28 12:17:45 +00:00

main: improve memory safety

Guys it's memory safe guys its's MEMORY SA- *shot dead*
This commit is contained in:
NotAShelf 2025-05-05 01:41:58 +03:00
parent 28871c9f52
commit 2c6f84e8ac
No known key found for this signature in database
GPG key ID: 29D95B64378DB4BF

View file

@ -5,6 +5,7 @@ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
process::Command, process::Command,
string::{String, ToString}, string::{String, ToString},
sync::OnceLock,
thread, thread,
}; };
use yansi::Paint; use yansi::Paint;
@ -91,64 +92,91 @@ fn main() {
print_changes(changed, &pre, &post); print_changes(changed, &pre, &post);
if let Some((pre_handle, post_handle)) = closure_size_handles { if let Some((pre_handle, post_handle)) = closure_size_handles {
let pre_size = pre_handle.join().unwrap(); match (pre_handle.join(), post_handle.join()) {
let post_size = post_handle.join().unwrap(); (Ok(pre_size), Ok(post_size)) => {
println!("{}", "Closure Size:".underline().bold());
println!("{}", "Closure Size:".underline().bold()); println!("Before: {pre_size} MiB");
println!("Before: {pre_size} MiB"); println!("After: {post_size} MiB");
println!("After: {post_size} MiB"); println!("Difference: {} MiB", post_size - pre_size);
println!("Difference: {} MiB", post_size - pre_size); }
_ => {
eprintln!("Error: Failed to get closure size information due to a thread error");
}
}
} }
} }
// gets the packages in a closure // gets the packages in a closure
fn get_packages(path: &std::path::Path) -> Vec<String> { fn get_packages(path: &std::path::Path) -> Vec<String> {
// get the nix store paths using nix-store --query --references <path> // get the nix store paths using nix-store --query --references <path>
let references = Command::new("nix-store") let output = Command::new("nix-store")
.arg("--query") .arg("--query")
.arg("--references") .arg("--references")
.arg(path.join("sw")) .arg(path.join("sw"))
.output(); .output();
if let Ok(query) = references { match output {
let list = str::from_utf8(&query.stdout); Ok(query) => {
match str::from_utf8(&query.stdout) {
if let Ok(list) = list { Ok(list) => list.lines().map(ToString::to_string).collect(),
let res: Vec<String> = list.lines().map(ToString::to_string).collect(); Err(e) => {
return res; eprintln!("Error decoding command output: {}", e);
Vec::new()
}
}
}
Err(e) => {
eprintln!("Error executing nix-store command: {}", e);
Vec::new()
} }
} }
Vec::new()
} }
// gets the dependencies of the packages in a closure // gets the dependencies of the packages in a closure
fn get_dependencies(path: &std::path::Path) -> Vec<String> { fn get_dependencies(path: &std::path::Path) -> Vec<String> {
// get the nix store paths using nix-store --query --references <path> // get the nix store paths using nix-store --query --references <path>
let references = Command::new("nix-store") let output = Command::new("nix-store")
.arg("--query") .arg("--query")
.arg("--requisites") .arg("--requisites")
.arg(path.join("sw")) .arg(path.join("sw"))
.output(); .output();
if let Ok(query) = references { match output {
let list = str::from_utf8(&query.stdout); Ok(query) => {
match str::from_utf8(&query.stdout) {
if let Ok(list) = list { Ok(list) => list.lines().map(ToString::to_string).collect(),
let res: Vec<String> = list.lines().map(ToString::to_string).collect(); Err(e) => {
return res; eprintln!("Error decoding command output: {}", e);
Vec::new()
}
}
}
Err(e) => {
eprintln!("Error executing nix-store command: {}", e);
Vec::new()
} }
} }
Vec::new() }
// Returns a reference to the compiled regex pattern.
// The regex is compiled only once.
fn store_path_regex() -> &'static Regex {
static REGEX: OnceLock<Regex> = OnceLock::new();
REGEX.get_or_init(|| {
Regex::new(r"^/nix/store/[a-z0-9]+-(.+?)-([0-9].*?)$")
.expect("Failed to compile regex pattern for nix store paths")
})
} }
fn get_version<'a>(pack: impl Into<&'a str>) -> (&'a str, &'a str) { fn get_version<'a>(pack: impl Into<&'a str>) -> (&'a str, &'a str) {
// funny regex to split a nix store path into its name and its version. // funny regex to split a nix store path into its name and its version.
let re = Regex::new(r"^/nix/store/[a-z0-9]+-(.+?)-([0-9].*?)$").unwrap(); let path = pack.into();
// No cap frfr // Match the regex against the input
if let Some(cap) = re.captures(pack.into()) { if let Some(cap) = store_path_regex().captures(path) {
let name = cap.get(1).unwrap().as_str(); // Handle potential missing captures safely
let version = cap.get(2).unwrap().as_str(); let name = cap.get(1).map_or("", |m| m.as_str());
let version = cap.get(2).map_or("", |m| m.as_str());
return (name, version); return (name, version);
} }
@ -164,21 +192,54 @@ fn check_nix_available() -> bool {
} }
fn get_closure_size(path: &std::path::Path) -> i64 { fn get_closure_size(path: &std::path::Path) -> i64 {
Command::new("nix") // Run nix path-info command to get closure size
match Command::new("nix")
.arg("path-info") .arg("path-info")
.arg("--closure-size") .arg("--closure-size")
.arg(path.join("sw")) .arg(path.join("sw"))
.output() .output()
.ok() {
.and_then(|output| { Ok(output) if output.status.success() => {
str::from_utf8(&output.stdout) // Parse command output to extract the size
.ok()? match str::from_utf8(&output.stdout) {
.split_whitespace() Ok(stdout) => {
.last()? // Parse the last word in the output as an integer
.parse::<i64>() stdout
.ok() .split_whitespace()
}) .last()
.map_or(0, |size| size / 1024 / 1024) .and_then(|s| s.parse::<i64>().ok())
.map_or_else(
|| {
eprintln!("Failed to parse closure size from output: {}", stdout);
0
},
|size| size / 1024 / 1024, // Convert to MiB
)
}
Err(e) => {
eprintln!("Error decoding command output: {}", e);
0
}
}
}
Ok(output) => {
// Command ran but returned an error
match str::from_utf8(&output.stderr) {
Ok(stderr) if !stderr.is_empty() => {
eprintln!("nix path-info command failed: {}", stderr);
}
_ => {
eprintln!("nix path-info command failed with status: {}", output.status);
}
}
0
}
Err(e) => {
// Command failed to run
eprintln!("Failed to execute nix path-info command: {}", e);
0
}
}
} }
fn print_added(set: HashSet<&str>, post: &HashMap<&str, HashSet<&str>>) { fn print_added(set: HashSet<&str>, post: &HashMap<&str, HashSet<&str>>) {
@ -224,21 +285,21 @@ fn print_changes(
continue; continue;
} }
// can not fail since maybe_changed is the union of the keys of pre and post // We should handle the case where the package might not exist in one of the maps
let ver_pre = pre.get(p).unwrap(); if let (Some(ver_pre), Some(ver_post)) = (pre.get(p), post.get(p)) {
let ver_post = post.get(p).unwrap(); let version_str_pre = ver_pre.iter().copied().collect::<Vec<_>>().join(" ");
let version_str_pre = ver_pre.iter().copied().collect::<Vec<_>>().join(" "); let version_str_post = ver_post.iter().copied().collect::<Vec<_>>().join(", ");
let version_str_post = ver_post.iter().copied().collect::<Vec<_>>().join(", ");
if ver_pre != ver_post { if ver_pre != ver_post {
println!( println!(
"{} {} {} {} ~> {}", "{} {} {} {} ~> {}",
"[C:]".bold().bright_yellow(), "[C:]".bold().bright_yellow(),
p, p,
"@".yellow(), "@".yellow(),
version_str_pre.magenta(), version_str_pre.magenta(),
version_str_post.blue() version_str_post.blue()
); );
}
} }
} }
} }