use thiserror::Error;
#[derive(Debug, Error)]
pub enum BuilderError {
#[error("Validation error: {message}. Suggestion: {suggestion}")]
ValidationError { message: String, suggestion: String },
#[error("Missing required field: {field}. Call {suggestion} first")]
MissingField { field: String, suggestion: String },
#[error("Invalid entity reference: '{entity}'. Available entities: {available}")]
InvalidEntityRef { entity: String, available: String },
#[error("Constraint violation: {constraint}. Details: {details}")]
ConstraintViolation { constraint: String, details: String },
#[error("OpenSCENARIO error: {0}")]
OpenScenarioError(#[from] crate::error::Error),
}
impl BuilderError {
pub fn validation_error(message: &str) -> Self {
Self::ValidationError {
message: message.to_string(),
suggestion: "Check the documentation for valid values".to_string(),
}
}
pub fn validation_error_with_suggestion(message: &str, suggestion: &str) -> Self {
Self::ValidationError {
message: message.to_string(),
suggestion: suggestion.to_string(),
}
}
pub fn missing_field(field: &str, suggestion: &str) -> Self {
Self::MissingField {
field: field.to_string(),
suggestion: suggestion.to_string(),
}
}
pub fn invalid_entity_ref(entity: &str, available: &[String]) -> Self {
Self::InvalidEntityRef {
entity: entity.to_string(),
available: available.join(", "),
}
}
pub fn constraint_violation(constraint: &str, details: &str) -> Self {
Self::ConstraintViolation {
constraint: constraint.to_string(),
details: details.to_string(),
}
}
}
pub type BuilderResult<T> = Result<T, BuilderError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validation_error_display_includes_suggestion() {
let err = BuilderError::validation_error("bad value");
let msg = err.to_string();
assert!(msg.contains("bad value"));
assert!(msg.contains("Suggestion"));
}
#[test]
fn test_missing_field_display() {
let err = BuilderError::missing_field("name", "with_name()");
let msg = err.to_string();
assert!(msg.contains("name"));
assert!(msg.contains("with_name()"));
}
#[test]
fn test_invalid_entity_ref_joins_available() {
let err = BuilderError::invalid_entity_ref(
"ghost",
&["ego".to_string(), "lead".to_string()],
);
let msg = err.to_string();
assert!(msg.contains("ghost"));
assert!(msg.contains("ego, lead"));
}
#[test]
fn test_constraint_violation_display() {
let err = BuilderError::constraint_violation("speed > 0", "speed was -1");
let msg = err.to_string();
assert!(msg.contains("speed > 0"));
assert!(msg.contains("speed was -1"));
}
#[test]
fn test_validation_error_with_custom_suggestion() {
let err = BuilderError::validation_error_with_suggestion("oops", "try X");
let msg = err.to_string();
assert!(msg.contains("oops"));
assert!(msg.contains("try X"));
}
}