feat: use humansize crate and which crate for program lookup

Co-authored-by: aider (openai/andrew/openrouter/qwen/qwen3-coder) <aider@aider.chat>
This commit is contained in:
Andrew Phillips
2025-08-12 16:23:11 -03:00
parent 465e4c40ab
commit 6e4b690bd8
4 changed files with 9 additions and 139 deletions

View File

@@ -48,7 +48,8 @@ axum = "0.7"
tower = "0.4" tower = "0.4"
tower-http = { version = "0.5", features = ["cors", "fs", "trace"] } tower-http = { version = "0.5", features = ["cors", "fs", "trace"] }
hyper = { version = "1.0", features = ["full"] } hyper = { version = "1.0", features = ["full"] }
once_cell = "1.19.0" once_cell = "1.19.0" once_cell = "1.19.0"
once_cell = "1.19.0"which = "6.0"
[dev-dependencies] [dev-dependencies]
tempfile = "3.3.0" tempfile = "3.3.0"

View File

@@ -1,15 +1,10 @@
use anyhow::{Context, Result, anyhow}; use anyhow::{Context, Result, anyhow};
use log::*; use log::*;
use std::env;
use std::fs;
use std::fs::File; use std::fs::File;
use std::io::{Read, Write}; use std::io::{Read, Write};
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::{Child, Command, Stdio}; use std::process::{Child, Command, Stdio};
use std::collections::HashMap; use which::which;
use std::sync::Mutex;
use once_cell::sync::Lazy;
use crate::compression_engine::CompressionEngine; use crate::compression_engine::CompressionEngine;
@@ -69,11 +64,11 @@ impl CompressionEngineProgram {
compress: Vec<&str>, compress: Vec<&str>,
decompress: Vec<&str>, decompress: Vec<&str>,
) -> CompressionEngineProgram { ) -> CompressionEngineProgram {
let program_path = get_program_path(program); let program_path = which(program);
let supported = program_path.is_ok(); let supported = program_path.is_ok();
CompressionEngineProgram { CompressionEngineProgram {
program: program_path.unwrap_or(program.to_string()), program: program_path.map_or_else(|_| program.to_string(), |p| p.to_string_lossy().to_string()),
compress: compress.iter().map(|s| s.to_string()).collect(), compress: compress.iter().map(|s| s.to_string()).collect(),
decompress: decompress.iter().map(|s| s.to_string()).collect(), decompress: decompress.iter().map(|s| s.to_string()).collect(),
supported, supported,
@@ -81,54 +76,6 @@ impl CompressionEngineProgram {
} }
} }
// Cache for program lookup results to avoid redundant filesystem operations
static PROGRAM_CACHE: Lazy<Mutex<HashMap<String, Option<String>>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
fn get_program_path(program: &str) -> Result<String> {
// Check cache first
{
let cache = PROGRAM_CACHE.lock().unwrap();
if let Some(result) = cache.get(program) {
return match result {
Some(path) => Ok(path.clone()),
None => Err(anyhow!("Unable to find binary {} in PATH", program)),
};
}
}
debug!("COMPRESSION: Looking for executable: {}", program);
let result = if let Ok(path) = env::var("PATH") {
let mut found_path = None;
for p in path.split(':') {
let p_str = format!("{}/{}", p, program);
if let Ok(stat) = fs::metadata(&p_str) {
let permissions = stat.permissions();
if stat.is_file() && permissions.mode() & 0o111 != 0 {
found_path = Some(p_str);
break;
}
}
}
found_path
} else {
None
};
// Store result in cache
let mut cache = PROGRAM_CACHE.lock().unwrap();
match &result {
Some(path) => {
cache.insert(program.to_string(), Some(path.clone()));
Ok(path.clone())
}
None => {
cache.insert(program.to_string(), None);
Err(anyhow!("Unable to find binary {} in PATH", program))
}
}
}
impl CompressionEngine for CompressionEngineProgram { impl CompressionEngine for CompressionEngineProgram {
fn is_supported(&self) -> bool { fn is_supported(&self) -> bool {
self.supported self.supported

View File

@@ -1,15 +1,10 @@
use crate::plugins::ProgramWriter; use crate::plugins::ProgramWriter;
use anyhow::{Context, Result, anyhow}; use anyhow::{Context, Result, anyhow};
use log::*; use log::*;
use std::env;
use std::fs;
use std::io; use std::io;
use std::io::Write; use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::collections::HashMap; use which::which;
use std::sync::Mutex;
use once_cell::sync::Lazy;
use crate::meta_plugin::MetaPlugin; use crate::meta_plugin::MetaPlugin;
@@ -25,11 +20,11 @@ pub struct MetaPluginProgram {
impl MetaPluginProgram { impl MetaPluginProgram {
pub fn new(program: &str, args: Vec<&str>, meta_name: String, split_whitespace: bool) -> MetaPluginProgram { pub fn new(program: &str, args: Vec<&str>, meta_name: String, split_whitespace: bool) -> MetaPluginProgram {
let program_path = get_program_path(program); let program_path = which(program);
let supported = program_path.is_ok(); let supported = program_path.is_ok();
MetaPluginProgram { MetaPluginProgram {
program: program_path.unwrap_or(program.to_string()), program: program_path.map_or_else(|_| program.to_string(), |p| p.to_string_lossy().to_string()),
args: args.iter().map(|s| s.to_string()).collect(), args: args.iter().map(|s| s.to_string()).collect(),
supported, supported,
meta_name, meta_name,
@@ -134,51 +129,3 @@ impl MetaPlugin for MetaPluginProgram {
} }
} }
// Cache for program lookup results to avoid redundant filesystem operations
static PROGRAM_CACHE: Lazy<Mutex<HashMap<String, Option<String>>>> =
Lazy::new(|| Mutex::new(HashMap::new()));
fn get_program_path(program: &str) -> Result<String> {
// Check cache first
{
let cache = PROGRAM_CACHE.lock().unwrap();
if let Some(result) = cache.get(program) {
return match result {
Some(path) => Ok(path.clone()),
None => Err(anyhow!("Unable to find binary {} in PATH", program)),
};
}
}
debug!("META: Looking for executable: {}", program);
let result = if let Ok(path) = env::var("PATH") {
let mut found_path = None;
for p in path.split(':') {
let p_str = format!("{}/{}", p, program);
if let Ok(stat) = fs::metadata(&p_str) {
let permissions = stat.permissions();
if stat.is_file() && permissions.mode() & 0o111 != 0 {
found_path = Some(p_str);
break;
}
}
}
found_path
} else {
None
};
// Store result in cache
let mut cache = PROGRAM_CACHE.lock().unwrap();
match &result {
Some(path) => {
cache.insert(program.to_string(), Some(path.clone()));
Ok(path.clone())
}
None => {
cache.insert(program.to_string(), None);
Err(anyhow!("Unable to find binary {} in PATH", program))
}
}
}

View File

@@ -29,34 +29,9 @@ pub fn get_meta_from_env() -> HashMap<String, String> {
meta_env meta_env
} }
pub fn format_size_human_readable(size: u64) -> String {
const UNITS: &[&str] = &["", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"];
const THRESHOLD: u64 = 1024;
if size == 0 {
return "0".to_string();
}
let mut size_f = size as f64;
let mut unit_index = 0;
while size_f >= THRESHOLD as f64 && unit_index < UNITS.len() - 1 {
size_f /= THRESHOLD as f64;
unit_index += 1;
}
if unit_index == 0 {
format!("{}", size)
} else if size_f.fract() == 0.0 {
format!("{}{}", size_f as u64, UNITS[unit_index])
} else {
format!("{:.1}{}", size_f, UNITS[unit_index])
}
}
pub fn format_size(size: u64, human_readable: bool) -> String { pub fn format_size(size: u64, human_readable: bool) -> String {
match human_readable { match human_readable {
true => format_size_human_readable(size), true => humansize::format_size(size, humansize::DECIMAL),
false => size.to_string(), false => size.to_string(),
} }
} }