use crate::error::{KopiError, Result};
use std::fs;
use std::path::{Path, PathBuf};
pub fn sanitize_segment(value: &str) -> Option<String> {
let mut output = String::with_capacity(value.len());
let mut last_dash = false;
for ch in value.chars() {
if ch.is_ascii_alphanumeric() {
output.push(ch.to_ascii_lowercase());
last_dash = false;
} else if !last_dash {
output.push('-');
last_dash = true;
}
}
let trimmed = output.trim_matches('-');
if trimmed.is_empty() {
None
} else {
Some(trimmed.to_string())
}
}
pub fn ensure_directory(path: PathBuf) -> Result<PathBuf> {
fs::create_dir_all(&path).map_err(|error| {
KopiError::ConfigError(format!(
"Failed to create directory {}: {error}",
path.display()
))
})?;
Ok(path)
}
pub fn ensure_child_directory(root: &Path, child: &str) -> Result<PathBuf> {
ensure_directory(root.join(child))
}
pub fn ensure_nested_directory<'a, I>(root: &Path, segments: I) -> Result<PathBuf>
where
I: IntoIterator<Item = &'a str>,
{
let mut path = PathBuf::from(root);
for segment in segments {
path.push(segment);
}
ensure_directory(path)
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn sanitize_segment_matches_legacy_behaviour() {
assert_eq!(sanitize_segment(" Tem urin "), Some("tem-urin".to_string()));
assert_eq!(sanitize_segment("***"), None);
assert_eq!(
sanitize_segment("Zulu-21.0.1+35.1"),
Some("zulu-21-0-1-35-1".to_string())
);
}
#[test]
fn ensure_nested_directory_creates_full_path() {
let temp_dir = TempDir::new().unwrap();
let nested = ensure_nested_directory(temp_dir.path(), ["locks", "install"]).unwrap();
let mut expected = temp_dir.path().to_path_buf();
expected.push("locks");
expected.push("install");
assert_eq!(nested, expected);
assert!(nested.exists());
}
}