1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! Input validation API (Multi-line editing)

use crate::keymap::Invoke;
use crate::Result;

/// Input validation result
#[non_exhaustive]
pub enum ValidationResult {
    /// Incomplete input
    Incomplete,
    /// Validation fails with an optional error message. User must fix the
    /// input.
    Invalid(Option<String>),
    /// Validation succeeds with an optional message
    Valid(Option<String>),
}

impl ValidationResult {
    pub(crate) fn is_valid(&self) -> bool {
        matches!(self, ValidationResult::Valid(_))
    }

    pub(crate) fn has_message(&self) -> bool {
        matches!(
            self,
            ValidationResult::Valid(Some(_)) | ValidationResult::Invalid(Some(_))
        )
    }
}

/// Give access to user input.
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 }
    }

    /// Returns user input.
    pub fn input(&self) -> &str {
        self.i.input()
    }

    // TODO
    //fn invoke(&mut self, cmd: Cmd) -> Result<?> {
    //    self.i.invoke(cmd)
    //}
}

/// This trait provides an extension interface for determining whether
/// the current input buffer is valid. Rustyline uses the method
/// provided by this trait to decide whether hitting the enter key
/// will end the current editing session and return the current line
/// buffer to the caller of `Editor::readline` or variants.
pub trait Validator {
    /// Takes the currently edited `input` and returns a
    /// `ValidationResult` indicating whether it is valid or not along
    /// with an option message to display about the result. The most
    /// common validity check to implement is probably whether the
    /// input is complete or not, for instance ensuring that all
    /// delimiters are fully balanced.
    ///
    /// If you implement more complex validation checks it's probably
    /// a good idea to also implement a `Hinter` to provide feedback
    /// about what is invalid.
    ///
    /// For auto-correction like a missing closing quote or to reject invalid
    /// char while typing, the input will be mutable (TODO).
    fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
        let _ = ctx;
        Ok(ValidationResult::Valid(None))
    }

    /// Configure whether validation is performed while typing or only
    /// when user presses the Enter key.
    ///
    /// Default is `false`.
    ///
    /// This feature is not yet implemented, so this function is currently a
    /// no-op
    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()
    }
}

/// Simple matching bracket validator.
#[derive(Default)]
pub struct MatchingBracketValidator {
    _priv: (),
}

impl MatchingBracketValidator {
    /// Constructor
    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
    }
}