initiative_core/command/token/
any_of.rs1use crate::app::AppMeta;
2use crate::command::prelude::*;
3
4use std::pin::Pin;
5
6use async_stream::stream;
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 tokens = if let TokenType::AnyOf(tokens) = &token.token_type {
18 tokens.iter().collect()
19 } else {
20 unreachable!();
21 };
22
23 match_input_with_tokens(token, input, app_meta, tokens)
24}
25
26pub fn match_input_with_tokens<'a, 'b>(
27 token: &'a Token,
28 input: &'a str,
29 app_meta: &'b AppMeta,
30 tokens: Vec<&'a Token>,
31) -> Pin<Box<dyn Stream<Item = FuzzyMatch<'a>> + 'b>>
32where
33 'a: 'b,
34{
35 Box::pin(stream! {
36 for (test_token_index, test_token) in tokens.iter().enumerate() {
38 for await fuzzy_match in test_token.match_input(input, app_meta) {
39 match fuzzy_match {
40 FuzzyMatch::Partial(token_match, completion) => {
42 yield FuzzyMatch::Partial(
43 TokenMatch::new(token, vec![token_match]),
44 completion,
45 );
46 }
47
48 FuzzyMatch::Exact(token_match) => {
50 yield FuzzyMatch::Exact(TokenMatch::new(token, vec![token_match]));
51 }
52
53 FuzzyMatch::Overflow(token_match, remainder) if tokens.len() == 1 => {
56 yield FuzzyMatch::Overflow(
57 TokenMatch::new(token, vec![token_match]),
58 remainder,
59 );
60 }
61
62 FuzzyMatch::Overflow(token_match, remainder) => {
65 let remainder_str = remainder.as_str();
66
67 yield FuzzyMatch::Overflow(
68 TokenMatch::new(token, vec![token_match.clone()]),
69 remainder,
70 );
71
72 let next_tokens: Vec<_> = tokens
73 .iter()
74 .take(test_token_index)
75 .chain(tokens.iter().skip(test_token_index + 1))
76 .copied()
77 .collect();
78
79 for await next_fuzzy_match in
80 match_input_with_tokens(token, remainder_str, app_meta, next_tokens)
81 {
82 yield next_fuzzy_match.map(|next_match| {
83 let mut next_meta_sequence = next_match.match_meta.into_sequence().unwrap();
84 next_meta_sequence.insert(0, token_match.clone());
85
86 TokenMatch::new(token, next_meta_sequence)
87 });
88 }
89 }
90 }
91 }
92 }
93 })
94}
95
96#[cfg(test)]
97mod test {
98 use super::*;
99
100 use crate::test_utils as test;
101
102 #[derive(Hash)]
103 enum Marker {
104 Phrase,
105 Keyword,
106 AnyWord,
107 }
108
109 #[tokio::test]
110 async fn match_input_test_partial() {
111 let tokens = [keyword("badger"), keyword("mushroom"), keyword("snake")];
112 let keyword_token = tokens[1].clone();
113 let any_of_token = any_of(tokens);
114
115 test::assert_eq_unordered!(
116 [FuzzyMatch::Partial(
117 TokenMatch::new(&any_of_token, vec![TokenMatch::from(&keyword_token)]),
118 Some("room".to_string())
119 )],
120 any_of_token
121 .match_input("mush", &test::app_meta())
122 .collect::<Vec<_>>()
123 .await,
124 );
125 }
126
127 #[tokio::test]
128 async fn match_input_test_exact() {
129 let tokens = [
130 keyword_m(Marker::Keyword, "badger"),
131 any_word_m(Marker::AnyWord),
132 ];
133 let [keyword_token, any_word_token] = tokens.clone();
134
135 let any_of_token = any_of_m(Marker::Phrase, tokens);
136
137 test::assert_eq_unordered!(
138 [
139 FuzzyMatch::Overflow(
140 TokenMatch::new(&any_of_token, vec![TokenMatch::from(&keyword_token)]),
141 " badger".into(),
142 ),
143 FuzzyMatch::Overflow(
144 TokenMatch::new(
145 &any_of_token,
146 vec![TokenMatch::new(&any_word_token, "badger")],
147 ),
148 " badger".into(),
149 ),
150 FuzzyMatch::Exact(TokenMatch::new(
151 &any_of_token,
152 vec![
153 TokenMatch::from(&keyword_token),
154 TokenMatch::new(&any_word_token, "badger"),
155 ],
156 )),
157 FuzzyMatch::Exact(TokenMatch::new(
158 &any_of_token,
159 vec![
160 TokenMatch::new(&any_word_token, "badger"),
161 TokenMatch::from(&keyword_token),
162 ],
163 )),
164 ],
165 any_of_token
166 .match_input("badger badger", &test::app_meta())
167 .collect::<Vec<_>>()
168 .await,
169 );
170 }
171
172 #[tokio::test]
173 async fn match_input_test_exact_overflow() {
174 let tokens = [keyword("badger"), keyword("mushroom"), keyword("snake")];
175 let [badger_token, mushroom_token, _] = tokens.clone();
176 let any_of_token = any_of_m(Marker::Phrase, tokens);
177
178 test::assert_eq_unordered!(
179 [
180 FuzzyMatch::Overflow(
181 TokenMatch::new(&any_of_token, vec![TokenMatch::from(&badger_token)]),
182 " mushroom".into(),
183 ),
184 FuzzyMatch::Exact(TokenMatch::new(
185 &any_of_token,
186 vec![
187 TokenMatch::from(&badger_token),
188 TokenMatch::from(&mushroom_token)
189 ]
190 )),
191 ],
192 any_of_token
193 .match_input("badger mushroom", &test::app_meta())
194 .collect::<Vec<_>>()
195 .await,
196 );
197 }
198
199 #[tokio::test]
200 async fn match_input_test_overflow() {
201 let badger_token = keyword("badger");
202 let any_of_token = any_of([badger_token.clone()]);
203
204 test::assert_eq_unordered!(
205 [FuzzyMatch::Overflow(
206 TokenMatch::new(&any_of_token, vec![TokenMatch::from(&badger_token)]),
207 " badger".into(),
208 )],
209 any_of_token
210 .match_input("badger badger", &test::app_meta())
211 .collect::<Vec<_>>()
212 .await,
213 );
214 }
215}