use crate::error::{PluginManagerError, PluginManagerResult};
use super::constants::MAX_PLUGIN_NAME_LEN;
use std::path::Path;
pub fn sanitize_path_string(path: &str, allow_slash: bool) -> String {
path.chars()
.filter(|c| {
c.is_alphanumeric()
|| *c == '-'
|| *c == '_'
|| (allow_slash && (*c == '/' || *c == '\\'))
})
.collect::<String>()
}
pub fn sanitize_plugin_name(name: &str) -> PluginManagerResult<String> {
let sanitized = sanitize_path_string(name, false);
if sanitized.is_empty() {
return Err(PluginManagerError::ValidationFailed("插件名称无效:清理后为空".to_string()));
}
if sanitized.len() > MAX_PLUGIN_NAME_LEN {
return Err(PluginManagerError::ValidationFailed(
format!("插件名称过长:超过{}个字符", MAX_PLUGIN_NAME_LEN)
));
}
Ok(sanitized)
}
pub fn validate_path_within_base(
base_path: &Path,
target_path: &Path,
) -> PluginManagerResult<()> {
let canonical_base = base_path.canonicalize()
.map_err(|e| PluginManagerError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("无法规范化基础路径: {}", e)
)))?;
let canonical_target = target_path.canonicalize()
.or_else(|e| {
if let Some(parent) = target_path.parent() {
parent.canonicalize()
.map(|p| {
if let Some(file_name) = target_path.file_name() {
p.join(file_name)
} else {
p
}
})
.map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("无法规范化目标路径或其父目录: {} (原始错误: {})", target_path.display(), e)
)
})
} else {
Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("无法规范化目标路径且没有父目录: {} (原始错误: {})", target_path.display(), e)
))
}
})
.map_err(|e| PluginManagerError::Io(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("无法规范化目标路径: {}", e)
)))?;
if !canonical_target.starts_with(&canonical_base) {
return Err(PluginManagerError::ValidationFailed(
"路径验证失败:目标路径不在基础路径内".to_string()
));
}
Ok(())
}