rustyline/validate.rs
1//! Input validation API (Multi-line editing)
2
3use crate::keymap::Invoke;
4use crate::Result;
5
6/// Input validation result
7#[non_exhaustive]
8pub enum ValidationResult {
9 /// Incomplete input
10 Incomplete,
11 /// Validation fails with an optional error message. User must fix the
12 /// input.
13 Invalid(Option<String>),
14 /// Validation succeeds with an optional message
15 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
31/// Give access to user input.
32pub 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 /// Returns user input.
42 #[must_use]
43 pub fn input(&self) -> &str {
44 self.i.input()
45 }
46
47 // TODO
48 //fn invoke(&mut self, cmd: Cmd) -> Result<?> {
49 // self.i.invoke(cmd)
50 //}
51}
52
53/// This trait provides an extension interface for determining whether
54/// the current input buffer is valid. Rustyline uses the method
55/// provided by this trait to decide whether hitting the enter key
56/// will end the current editing session and return the current line
57/// buffer to the caller of `Editor::readline` or variants.
58pub trait Validator {
59 /// Takes the currently edited `input` and returns a
60 /// `ValidationResult` indicating whether it is valid or not along
61 /// with an option message to display about the result. The most
62 /// common validity check to implement is probably whether the
63 /// input is complete or not, for instance ensuring that all
64 /// delimiters are fully balanced.
65 ///
66 /// If you implement more complex validation checks it's probably
67 /// a good idea to also implement a `Hinter` to provide feedback
68 /// about what is invalid.
69 ///
70 /// For auto-correction like a missing closing quote or to reject invalid
71 /// char while typing, the input will be mutable (TODO).
72 fn validate(&self, ctx: &mut ValidationContext) -> Result<ValidationResult> {
73 let _ = ctx;
74 Ok(ValidationResult::Valid(None))
75 }
76
77 /// Configure whether validation is performed while typing or only
78 /// when user presses the Enter key.
79 ///
80 /// Default is `false`.
81 ///
82 /// This feature is not yet implemented, so this function is currently a
83 /// no-op
84 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/// Simple matching bracket validator.
102#[derive(Default)]
103pub struct MatchingBracketValidator {
104 _priv: (),
105}
106
107impl MatchingBracketValidator {
108 /// Constructor
109 #[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: {:?} is not properly closed",
131 wanted
132 )))
133 }
134 (None, c) => {
135 return ValidationResult::Invalid(Some(format!(
136 "Mismatched brackets: {:?} is unpaired",
137 c
138 )))
139 }
140 },
141 _ => {}
142 }
143 }
144 if stack.is_empty() {
145 ValidationResult::Valid(None)
146 } else {
147 ValidationResult::Incomplete
148 }
149}