Skip to main content

reovim_kernel/core/option/
constraint.rs

1//! Option value constraints and validation.
2
3use std::fmt;
4
5use super::value::OptionValue;
6
7/// Constraints for option value validation.
8///
9/// These constraints are checked when setting option values.
10/// Not all constraints apply to all value types.
11#[derive(Debug, Clone, Default, PartialEq, Eq)]
12pub struct OptionConstraint {
13    /// For Integer: minimum value (inclusive).
14    pub min: Option<i64>,
15    /// For Integer: maximum value (inclusive).
16    pub max: Option<i64>,
17    /// For String: minimum length.
18    pub min_length: Option<usize>,
19    /// For String: maximum length.
20    pub max_length: Option<usize>,
21}
22
23impl OptionConstraint {
24    /// Create a constraint with no restrictions.
25    #[must_use]
26    pub const fn none() -> Self {
27        Self {
28            min: None,
29            max: None,
30            min_length: None,
31            max_length: None,
32        }
33    }
34
35    /// Create a constraint with integer range (inclusive).
36    #[must_use]
37    pub const fn range(min: i64, max: i64) -> Self {
38        Self {
39            min: Some(min),
40            max: Some(max),
41            min_length: None,
42            max_length: None,
43        }
44    }
45
46    /// Create a constraint with minimum integer value.
47    #[must_use]
48    pub const fn min(min: i64) -> Self {
49        Self {
50            min: Some(min),
51            max: None,
52            min_length: None,
53            max_length: None,
54        }
55    }
56
57    /// Create a constraint with maximum integer value.
58    #[must_use]
59    pub const fn max(max: i64) -> Self {
60        Self {
61            min: None,
62            max: Some(max),
63            min_length: None,
64            max_length: None,
65        }
66    }
67
68    /// Create a constraint with string length range.
69    #[must_use]
70    pub const fn string_length(min_length: usize, max_length: usize) -> Self {
71        Self {
72            min: None,
73            max: None,
74            min_length: Some(min_length),
75            max_length: Some(max_length),
76        }
77    }
78
79    /// Validate a value against this constraint.
80    ///
81    /// # Errors
82    ///
83    /// Returns `ConstraintError` if the value violates the constraint.
84    pub fn validate(&self, value: &OptionValue) -> Result<(), ConstraintError> {
85        match value {
86            OptionValue::Integer(i) => {
87                if let Some(min) = self.min
88                    && *i < min
89                {
90                    return Err(ConstraintError::BelowMinimum { value: *i, min });
91                }
92                if let Some(max) = self.max
93                    && *i > max
94                {
95                    return Err(ConstraintError::AboveMaximum { value: *i, max });
96                }
97            }
98            OptionValue::String(s) => {
99                if let Some(min_len) = self.min_length
100                    && s.len() < min_len
101                {
102                    return Err(ConstraintError::StringTooShort {
103                        len: s.len(),
104                        min: min_len,
105                    });
106                }
107                if let Some(max_len) = self.max_length
108                    && s.len() > max_len
109                {
110                    return Err(ConstraintError::StringTooLong {
111                        len: s.len(),
112                        max: max_len,
113                    });
114                }
115            }
116            OptionValue::Choice { value, choices } => {
117                if !choices.contains(value) {
118                    return Err(ConstraintError::InvalidChoice {
119                        value: value.clone(),
120                        choices: choices.clone(),
121                    });
122                }
123            }
124            OptionValue::Bool(_) => {
125                // No constraints for boolean values
126            }
127        }
128        Ok(())
129    }
130}
131
132/// Constraint validation error.
133#[derive(Debug, Clone, PartialEq, Eq)]
134pub enum ConstraintError {
135    /// Integer value is below minimum.
136    BelowMinimum {
137        /// The actual value
138        value: i64,
139        /// The minimum allowed
140        min: i64,
141    },
142    /// Integer value is above maximum.
143    AboveMaximum {
144        /// The actual value
145        value: i64,
146        /// The maximum allowed
147        max: i64,
148    },
149    /// String is too short.
150    StringTooShort {
151        /// Actual length
152        len: usize,
153        /// Minimum required length
154        min: usize,
155    },
156    /// String is too long.
157    StringTooLong {
158        /// Actual length
159        len: usize,
160        /// Maximum allowed length
161        max: usize,
162    },
163    /// Choice value is not in the allowed choices.
164    InvalidChoice {
165        /// The invalid value
166        value: String,
167        /// The valid choices
168        choices: Vec<String>,
169    },
170}
171
172impl fmt::Display for ConstraintError {
173    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match self {
175            Self::BelowMinimum { value, min } => {
176                write!(f, "value {value} is below minimum {min}")
177            }
178            Self::AboveMaximum { value, max } => {
179                write!(f, "value {value} is above maximum {max}")
180            }
181            Self::StringTooShort { len, min } => {
182                write!(f, "string length {len} is below minimum {min}")
183            }
184            Self::StringTooLong { len, max } => {
185                write!(f, "string length {len} is above maximum {max}")
186            }
187            Self::InvalidChoice { value, choices } => {
188                write!(f, "'{value}' is not a valid choice (valid: {choices:?})")
189            }
190        }
191    }
192}
193
194impl std::error::Error for ConstraintError {}