1use crate::keymap::Invoke;
4use crate::Result;
5
6#[non_exhaustive]
8pub enum ValidationResult {
9 Incomplete,
11 Invalid(Option<String>),
14 Valid(Option<String>),
16}
17
18impl ValidationResult {
19 pub(crate) fn is_valid(&self) -> bool {
20 matches!(self, ValidationResult::Valid(_))
21 }
22
23 pub(crate) fn has_message(&self) -> bool {
24 matches!(
25 self,
26 ValidationResult::Valid(Some(_)) | ValidationResult::Invalid(Some(_))
27 )
28 }
29}
30
31pub struct ValidationContext<'i> {
33 i: &'i mut dyn Invoke,
34}
35
36impl<'i> ValidationContext<'i> {
37 pub(crate) fn new(i: &'i mut dyn Invoke) -> Self {
38 ValidationContext { i }
39 }
40
41 #[must_use]
43 pub fn input(&self) -> &str {
44 self.i.input()
45 }
46
47 }
52
53pub trait Validator {
59 fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
73 let _ = ctx;
74 Ok(ValidationResult::Valid(None))
75 }
76
77 fn validate_while_typing(&self) -> bool {
85 false
86 }
87}
88
89impl Validator for () {}
90
91impl<'v, V: ?Sized + Validator> Validator for &'v V {
92 fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
93 (**self).validate(ctx)
94 }
95
96 fn validate_while_typing(&self) -> bool {
97 (**self).validate_while_typing()
98 }
99}
100
101#[derive(Default)]
103pub struct MatchingBracketValidator {
104 _priv: (),
105}
106
107impl MatchingBracketValidator {
108 #[must_use]
110 pub fn new() -> Self {
111 Self { _priv: () }
112 }
113}
114
115impl Validator for MatchingBracketValidator {
116 fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
117 Ok(validate_brackets(ctx.input()))
118 }
119}
120
121fn validate_brackets(input: &str) -> ValidationResult {
122 let mut stack = vec![];
123 for c in input.chars() {
124 match c {
125 '(' | '[' | '{' => stack.push(c),
126 ')' | ']' | '}' => match (stack.pop(), c) {
127 (Some('('), ')') | (Some('['), ']') | (Some('{'), '}') => {}
128 (Some(wanted), _) => {
129 return ValidationResult::Invalid(Some(format!(
130 "Mismatched brackets: {wanted:?} is not properly closed"
131 )))
132 }
133 (None, c) => {
134 return ValidationResult::Invalid(Some(format!(
135 "Mismatched brackets: {c:?} is unpaired"
136 )))
137 }
138 },
139 _ => {}
140 }
141 }
142 if stack.is_empty() {
143 ValidationResult::Valid(None)
144 } else {
145 ValidationResult::Incomplete
146 }
147}