initiative_core/command/token/
keyword_list.rs

1use crate::command::prelude::*;
2use crate::utils::{quoted_words, CaseInsensitiveStr};
3
4use std::pin::Pin;
5
6use async_stream::stream;
7use futures::prelude::*;
8
9pub fn match_input<'a>(
10    token: &'a Token,
11    input: &'a str,
12) -> Pin<Box<dyn Stream<Item = FuzzyMatch<'a>> + 'a>> {
13    let TokenType::KeywordList(keywords) = &token.token_type else {
14        unreachable!();
15    };
16
17    Box::pin(stream! {
18        let mut iter = quoted_words(input).peekable();
19        if let Some(first_word) = iter.next() {
20            for keyword in keywords.iter() {
21                if keyword.eq_ci(first_word.as_str()) {
22                    if iter.peek().is_none() {
23                        yield FuzzyMatch::Exact(TokenMatch::new(token, first_word.as_str()));
24                    } else {
25                        yield FuzzyMatch::Overflow(
26                            TokenMatch::new(token, first_word.as_str()),
27                            first_word.after(),
28                        );
29                    }
30                } else if first_word.can_complete() {
31                    if let Some(completion) = keyword.strip_prefix_ci(&first_word) {
32                        yield FuzzyMatch::Partial(
33                            TokenMatch::new(token, first_word.as_str()),
34                            Some(completion.to_string()),
35                        );
36                    }
37                }
38            }
39        }
40    })
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    use crate::test_utils as test;
48
49    #[derive(Hash)]
50    enum Marker {
51        Token,
52    }
53
54    #[tokio::test]
55    async fn match_input_test_no_complete() {
56        let token = keyword_list(["badger", "mushroom", "snake", "badgering"]);
57
58        test::assert_eq_unordered!(
59            [FuzzyMatch::Exact(TokenMatch::new(&token, "badger"))],
60            token
61                .match_input("badger ", &test::app_meta())
62                .collect::<Vec<_>>()
63                .await,
64        );
65    }
66
67    #[tokio::test]
68    async fn match_input_test_partial() {
69        let token = keyword_list_m(Marker::Token, ["polyp", "POLYPHEMUS"]);
70
71        test::assert_eq_unordered!(
72            [
73                FuzzyMatch::Exact(TokenMatch::new(&token, "polyp")),
74                FuzzyMatch::Partial(TokenMatch::new(&token, "polyp"), Some("HEMUS".to_string())),
75            ],
76            token
77                .match_input("polyp", &test::app_meta())
78                .collect::<Vec<_>>()
79                .await,
80        );
81    }
82
83    #[tokio::test]
84    async fn match_input_test_overflow() {
85        let token = keyword_list(["badger", "mushroom"]);
86
87        test::assert_eq_unordered!(
88            [FuzzyMatch::Overflow(
89                TokenMatch::new(&token, "badger"),
90                " mushroom".into(),
91            )],
92            token
93                .match_input("badger mushroom", &test::app_meta())
94                .collect::<Vec<_>>()
95                .await,
96        );
97    }
98}