use std::path::Path;
use std::process::Output;
use crate::sdist::error::SdistError;
pub fn uv_version() -> Result<String, SdistError> {
let output = std::process::Command::new("uv")
.arg("--version")
.output()
.map_err(map_spawn_err)?;
if !output.status.success() {
return Err(SdistError::UvNotFound);
}
Ok(String::from_utf8_lossy(&output.stdout).trim().to_string())
}
pub fn uv_build_wheel(sdist_root: &Path, out_dir: &Path) -> Result<Output, SdistError> {
std::process::Command::new("uv")
.arg("build")
.arg("--wheel")
.arg("--out-dir")
.arg(out_dir)
.arg(sdist_root)
.output()
.map_err(map_spawn_err)
}
pub fn uv_lock(project_root: &Path) -> Result<std::process::ExitStatus, SdistError> {
std::process::Command::new("uv")
.arg("lock")
.current_dir(project_root)
.status()
.map_err(map_spawn_err)
}
fn map_spawn_err(e: std::io::Error) -> SdistError {
if e.kind() == std::io::ErrorKind::NotFound {
SdistError::UvNotFound
} else {
SdistError::UvSpawnFailed { source: e }
}
}
#[cfg(test)]
mod tests {
use super::*;
fn uv_on_path() -> bool {
std::process::Command::new("uv")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
#[test]
fn uv_version_returns_string_when_uv_present() {
if !uv_on_path() {
eprintln!("skipping: uv not on PATH");
return;
}
let v = uv_version().unwrap();
assert!(!v.is_empty(), "uv --version returned empty string");
}
#[test]
fn uv_version_returns_uv_not_found_when_path_empty() {
let result =
std::process::Command::new("nonexistent_binary_that_should_not_exist_42").output();
assert!(
result.is_err(),
"sanity check: nonexistent binary should fail to spawn"
);
}
}