use thiserror::Error;
use std::path::Path;
#[derive(Error, Debug)]
pub enum EspError {
#[error("Invalid file format")]
InvalidFormat,
#[error("Unsupported record type: {0}")]
UnsupportedRecordType(String),
#[error("Compression error: {0}")]
CompressionError(String),
#[error("IO error: {0}")]
IoError(#[from] std::io::Error),
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error),
}
struct StringValidationConfig {
blacklist: &'static [&'static str],
whitelist: &'static [&'static str],
}
impl StringValidationConfig {
const fn new() -> Self {
Self {
blacklist: &["<p>"],
whitelist: &["Orcax"],
}
}
}
pub fn is_valid_string(text: &str) -> bool {
let text = text.trim();
if text.is_empty() {
return false;
}
let config = StringValidationConfig::new();
if config.blacklist.contains(&text) {
return false;
}
if is_whitelisted(text, &config) {
return true;
}
if is_variable_name(text) {
return false;
}
text.chars().all(|c| !c.is_control() || c.is_whitespace())
}
fn is_whitelisted(text: &str, config: &StringValidationConfig) -> bool {
config.whitelist.iter().any(|&w| text.contains(w)) || text.contains("<Alias")
}
fn is_variable_name(text: &str) -> bool {
is_camel_case(text) || is_snake_case(text)
}
fn is_camel_case(text: &str) -> bool {
if text.len() < 3 || !text.chars().all(|c| c.is_ascii_alphanumeric()) {
return false;
}
let has_uppercase = text.chars().skip(2).any(|c| c.is_ascii_uppercase());
let not_all_uppercase = !text.chars().all(|c| c.is_ascii_uppercase());
has_uppercase && not_all_uppercase
}
fn is_snake_case(text: &str) -> bool {
!text.contains(' ') && text.contains('_')
}
pub fn create_backup(file_path: &Path) -> Result<std::path::PathBuf, EspError> {
if !file_path.exists() {
return Err(EspError::IoError(std::io::Error::new(
std::io::ErrorKind::NotFound,
"原文件不存在"
)));
}
let timestamp = chrono::Local::now().format("%Y-%m-%d-%H-%M-%S");
let backup_path = file_path.with_extension(format!("{}.bak", timestamp));
std::fs::copy(file_path, &backup_path)
.map_err(EspError::IoError)?;
Ok(backup_path)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_string_validation() {
assert!(is_valid_string("Iron Sword"));
assert!(is_valid_string("This is a valid description."));
assert!(is_valid_string("铁剑"));
assert!(is_valid_string("这是一个有效的描述。"));
assert!(is_valid_string("Mixed 中英文 text"));
assert!(!is_valid_string("CamelCaseVariable"));
assert!(!is_valid_string("snake_case_var"));
assert!(!is_valid_string(""));
assert!(!is_valid_string("<p>"));
}
#[test]
fn test_camel_case() {
assert!(is_camel_case("CamelCase"));
assert!(is_camel_case("myVariable"));
assert!(!is_camel_case("lowercase"));
assert!(!is_camel_case("UPPERCASE"));
assert!(!is_camel_case("my"));
}
#[test]
fn test_snake_case() {
assert!(is_snake_case("snake_case"));
assert!(is_snake_case("my_variable"));
assert!(!is_snake_case("normal text"));
assert!(!is_snake_case("CamelCase"));
}
}