raz_validation/
error.rs

1//! Validation error types
2
3use thiserror::Error;
4
5pub type ValidationResult<T> = Result<T, ValidationError>;
6
7#[derive(Error, Debug, Clone)]
8pub enum ValidationError {
9    #[error("Unknown option '{option}' for command '{command}'")]
10    UnknownOption {
11        command: String,
12        option: String,
13        suggestions: Vec<String>,
14    },
15
16    #[error("Invalid value '{value}' for option '{option}': {reason}")]
17    InvalidValue {
18        option: String,
19        value: String,
20        reason: String,
21    },
22
23    #[error("Option '{option}' conflicts with '{conflicts_with}'")]
24    OptionConflict {
25        option: String,
26        conflicts_with: String,
27    },
28
29    #[error("Option '{option}' requires '{required}' to be specified")]
30    MissingRequirement { option: String, required: String },
31
32    #[error("Option '{option}' is deprecated: {message}")]
33    DeprecatedOption { option: String, message: String },
34
35    #[error("Option '{option}' expects a value")]
36    MissingValue { option: String },
37
38    #[error("Option '{option}' does not accept a value")]
39    UnexpectedValue { option: String, value: String },
40
41    #[error("Provider error: {message}")]
42    ProviderError { message: String },
43}
44
45impl ValidationError {
46    /// Create an unknown option error with suggestions
47    pub fn unknown_option(
48        command: impl Into<String>,
49        option: impl Into<String>,
50        suggestions: Vec<String>,
51    ) -> Self {
52        Self::UnknownOption {
53            command: command.into(),
54            option: option.into(),
55            suggestions,
56        }
57    }
58
59    /// Create an invalid value error
60    pub fn invalid_value(
61        option: impl Into<String>,
62        value: impl Into<String>,
63        reason: impl Into<String>,
64    ) -> Self {
65        Self::InvalidValue {
66            option: option.into(),
67            value: value.into(),
68            reason: reason.into(),
69        }
70    }
71
72    /// Create a conflict error
73    pub fn conflict(option: impl Into<String>, conflicts_with: impl Into<String>) -> Self {
74        Self::OptionConflict {
75            option: option.into(),
76            conflicts_with: conflicts_with.into(),
77        }
78    }
79
80    /// Create a missing requirement error
81    pub fn missing_requirement(option: impl Into<String>, required: impl Into<String>) -> Self {
82        Self::MissingRequirement {
83            option: option.into(),
84            required: required.into(),
85        }
86    }
87
88    /// Create a deprecated option error
89    pub fn deprecated(option: impl Into<String>, message: impl Into<String>) -> Self {
90        Self::DeprecatedOption {
91            option: option.into(),
92            message: message.into(),
93        }
94    }
95
96    /// Get formatted error message with suggestions
97    pub fn format_with_suggestions(&self) -> String {
98        match self {
99            Self::UnknownOption {
100                command,
101                option,
102                suggestions,
103            } => {
104                let mut msg = format!("Unknown option '{option}' for command '{command}'");
105                if !suggestions.is_empty() {
106                    msg.push_str(&format!("\nDid you mean: {}", suggestions.join(", ")));
107                }
108                msg
109            }
110            _ => self.to_string(),
111        }
112    }
113}