initiative_core/command/token/
optional.rs1use crate::app::AppMeta;
2use crate::command::prelude::*;
3use crate::utils::quoted_words;
4
5use std::pin::Pin;
6
7use futures::prelude::*;
8
9pub fn match_input<'a, 'b>(
10 token: &'a Token,
11 input: &'a str,
12 app_meta: &'b AppMeta,
13) -> Pin<Box<dyn Stream<Item = FuzzyMatch<'a>> + 'b>>
14where
15 'a: 'b,
16{
17 let TokenType::Optional(optional_token) = &token.token_type else {
18 unreachable!();
19 };
20
21 Box::pin(
22 stream::iter([if quoted_words(input).next().is_none() {
23 FuzzyMatch::Exact(token.into())
24 } else {
25 FuzzyMatch::Overflow(token.into(), input.into())
26 }])
27 .chain(
28 optional_token
29 .match_input(input, app_meta)
30 .map(|fuzzy_match| {
31 fuzzy_match.map(|token_match| TokenMatch::new(token, token_match))
32 }),
33 ),
34 )
35}
36
37#[cfg(test)]
38mod test {
39 use super::*;
40
41 use crate::test_utils as test;
42
43 #[derive(Hash)]
44 enum Marker {
45 Optional,
46 Keyword,
47 }
48
49 #[tokio::test]
50 async fn match_input_test_simple() {
51 let keyword_token = keyword_m(Marker::Keyword, "badger");
52 let optional_token = optional_m(Marker::Optional, keyword_token.clone());
53
54 test::assert_eq_unordered!(
55 [
56 FuzzyMatch::Exact(TokenMatch::new(
57 &optional_token,
58 TokenMatch::from(&keyword_token)
59 )),
60 FuzzyMatch::Overflow(TokenMatch::from(&optional_token), "badger".into()),
61 ],
62 optional_token
63 .match_input("badger", &test::app_meta())
64 .collect::<Vec<_>>()
65 .await,
66 );
67 }
68
69 #[tokio::test]
70 async fn match_input_test_empty() {
71 let token = optional(keyword("badger"));
72
73 test::assert_eq_unordered!(
74 [FuzzyMatch::Exact(TokenMatch::from(&token))],
75 token
76 .match_input(" ", &test::app_meta())
77 .collect::<Vec<_>>()
78 .await,
79 );
80 }
81}