useras_errors::AppError;useregex::Regex;usecrate::domain::file::FileExtension;pubfnparse_filename(name:&str)->Result<(String, FileExtension), AppError>{if name.is_empty(){returnErr(AppError::ValidationError("filename empty".into()));}let re =Regex::new(r"^[A-Za-z0-9_\-]+\.[A-Za-z0-9]+$").map_err(|e|AppError::InternalError(format!("regex: {e}")))?;if!re.is_match(name){returnErr(AppError::ValidationError(format!("invalid filename '{name}'; allowed: [A-Za-z0-9_-] + extension")));}letmut parts = name.rsplitn(2,'.');let ext = parts.next().unwrap_or_default();let stem = parts.next().unwrap_or_default();let extension =FileExtension::parse(ext)?;Ok((stem.to_string(), extension))}#[must_use]pubfnsanitize(name:&str)-> String{letmut out =String::with_capacity(name.len());for c in name.chars(){if c.is_ascii_alphanumeric()|| c =='_'|| c =='-'|| c =='.'{
out.push(c);}elseif c ==''{
out.push('_');}}
out
}