use std::error::Error;
use std::fmt::{self, Display};
pub trait ScriptValidator {
type Error: Error + Display + Clone;
fn validate(&self, script: &str) -> Result<(), Self::Error>;
fn validate_batch<'a>(
&self,
scripts: &[(&'a str, &str)],
) -> Vec<(&'a str, Result<(), Self::Error>)> {
scripts
.iter()
.map(|(id, script)| (*id, self.validate(script)))
.collect()
}
}
#[derive(Debug, Clone)]
pub enum ScriptValidationError {
SyntaxError { engine: String, message: String },
MissingFunction { engine: String, function: String },
CompilationError { engine: String, message: String },
LoadError { engine: String, message: String },
UnsupportedEngine { engine: String },
}
impl Display for ScriptValidationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ScriptValidationError::SyntaxError { engine, message } => {
write!(f, "[{engine}] Syntax error: {message}")
}
ScriptValidationError::MissingFunction { engine, function } => {
write!(f, "[{engine}] Missing required function: {function}")
}
ScriptValidationError::CompilationError { engine, message } => {
write!(f, "[{engine}] Compilation error: {message}")
}
ScriptValidationError::LoadError { engine, message } => {
write!(f, "[{engine}] Load error: {message}")
}
ScriptValidationError::UnsupportedEngine { engine } => {
write!(f, "Unsupported script engine: {engine}")
}
}
}
}
impl Error for ScriptValidationError {}
impl ScriptValidationError {
pub fn engine(&self) -> &str {
match self {
ScriptValidationError::SyntaxError { engine, .. }
| ScriptValidationError::MissingFunction { engine, .. }
| ScriptValidationError::CompilationError { engine, .. }
| ScriptValidationError::LoadError { engine, .. }
| ScriptValidationError::UnsupportedEngine { engine } => engine,
}
}
pub fn message(&self) -> String {
match self {
ScriptValidationError::SyntaxError { message, .. } => message.clone(),
ScriptValidationError::MissingFunction { function, .. } => {
format!("Missing required function: {function}")
}
ScriptValidationError::CompilationError { message, .. } => message.clone(),
ScriptValidationError::LoadError { message, .. } => message.clone(),
ScriptValidationError::UnsupportedEngine { engine } => {
format!("Unsupported script engine: {engine}")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_script_validation_error_display() {
let err = ScriptValidationError::SyntaxError {
engine: "rhai".to_string(),
message: "unexpected token".to_string(),
};
assert_eq!(err.to_string(), "[rhai] Syntax error: unexpected token");
}
#[test]
fn test_script_validation_error_engine() {
let err = ScriptValidationError::MissingFunction {
engine: "javascript".to_string(),
function: "should_inject".to_string(),
};
assert_eq!(err.engine(), "javascript");
}
#[test]
fn test_script_validation_error_message() {
let err = ScriptValidationError::CompilationError {
engine: "lua".to_string(),
message: "failed to parse".to_string(),
};
assert_eq!(err.message(), "failed to parse");
}
}