use std::path::{Component, Path};
use crate::error::AppError;
const MAX_TENANT_ID_LEN: usize = 64;
pub fn tenant_id(raw: &str) -> Result<&str, AppError> {
let valid_length = !raw.is_empty() && raw.len() <= MAX_TENANT_ID_LEN;
let valid_chars = raw
.bytes()
.all(|byte| matches!(byte, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_'));
if valid_length && valid_chars {
Ok(raw)
} else {
Err(AppError::InvalidTenant {
tenant_id: raw.to_string(),
})
}
}
pub fn file_path(raw: &str) -> Result<&str, AppError> {
let path = raw.trim_start_matches('/');
if path.is_empty() {
return Err(AppError::InvalidPath {
reason: "path must not be empty".to_string(),
});
}
for component in Path::new(path).components() {
match component {
Component::ParentDir => {
return Err(AppError::InvalidPath {
reason: "path must not contain '..' components".to_string(),
});
}
Component::RootDir | Component::Prefix(_) => {
return Err(AppError::InvalidPath {
reason: "path must be relative".to_string(),
});
}
Component::Normal(name) if name == ".git" => {
return Err(AppError::InvalidPath {
reason: "path must not reference .git".to_string(),
});
}
_ => {}
}
}
Ok(path)
}