use crate::keymap::Invoke;
use crate::Result;
#[non_exhaustive]
pub enum ValidationResult {
Incomplete,
Invalid(Option<String>),
Valid(Option<String>),
}
pub struct ValidationContext<'i> {
i: &'i mut dyn Invoke,
}
impl<'i> ValidationContext<'i> {
pub(crate) fn new(i: &'i mut dyn Invoke) -> Self {
ValidationContext { i }
}
pub fn input(&self) -> &str {
self.i.input()
}
}
pub trait Validator {
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
let _ = ctx;
Ok(ValidationResult::Valid(None))
}
fn validate_while_typing(&self) -> bool {
false
}
}
impl Validator for () {}
impl<'v, V: ?Sized + Validator> Validator for &'v V {
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
(**self).validate(ctx)
}
fn validate_while_typing(&self) -> bool {
(**self).validate_while_typing()
}
}
#[derive(Default)]
pub struct MatchingBracketValidator {
_priv: (),
}
impl MatchingBracketValidator {
pub fn new() -> Self {
Self { _priv: () }
}
}
impl Validator for MatchingBracketValidator {
fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
Ok(validate_brackets(ctx.input()))
}
}
fn validate_brackets(input: &str) -> ValidationResult {
let mut stack = vec![];
for c in input.chars() {
match c {
'(' | '[' | '{' => stack.push(c),
')' | ']' | '}' => match (stack.pop(), c) {
(Some('('), ')') | (Some('['), ']') | (Some('{'), '}') => {}
(Some(wanted), _) => {
return ValidationResult::Invalid(Some(format!(
"Mismatched brackets: {:?} is not properly closed",
wanted
)))
}
(None, c) => {
return ValidationResult::Invalid(Some(format!(
"Mismatched brackets: {:?} is unpaired",
c
)))
}
},
_ => {}
}
}
if stack.is_empty() {
ValidationResult::Valid(None)
} else {
ValidationResult::Incomplete
}
}