embedded_cli/
command.rs

1use core::marker::PhantomData;
2
3use embedded_io::Write;
4
5use crate::{
6    arguments::ArgList,
7    cli::CliHandle,
8    service::{Autocomplete, CommandProcessor, FromRaw, Help, ParseError, ProcessError},
9    token::Tokens,
10};
11
12#[cfg(feature = "autocomplete")]
13use crate::autocomplete::{Autocompletion, Request};
14
15#[cfg(feature = "help")]
16use crate::service::HelpError;
17
18#[derive(Clone, Debug, Eq, PartialEq)]
19pub struct RawCommand<'a> {
20    /// Name of the command.
21    ///
22    /// In `set led 1 1` name is `set`
23    name: &'a str,
24
25    /// Argument list of the command
26    ///
27    /// In `set led 1 1` arguments is `led 1 1`
28    args: ArgList<'a>,
29}
30
31impl<'a> RawCommand<'a> {
32    /// Crate raw command from input tokens
33    pub(crate) fn from_tokens(tokens: &Tokens<'a>) -> Option<Self> {
34        let mut iter = tokens.iter();
35        let name = iter.next()?;
36        let tokens = iter.into_tokens();
37
38        Some(RawCommand {
39            name,
40            args: ArgList::new(tokens),
41        })
42    }
43
44    pub fn new(name: &'a str, args: ArgList<'a>) -> Self {
45        Self { name, args }
46    }
47
48    pub fn args(&self) -> ArgList<'a> {
49        self.args.clone()
50    }
51
52    pub fn name(&self) -> &'a str {
53        self.name
54    }
55
56    pub fn processor<
57        W: Write<Error = E>,
58        E: embedded_io::Error,
59        F: FnMut(&mut CliHandle<'_, W, E>, RawCommand<'_>) -> Result<(), E>,
60    >(
61        f: F,
62    ) -> impl CommandProcessor<W, E> {
63        struct Processor<
64            W: Write<Error = E>,
65            E: embedded_io::Error,
66            F: FnMut(&mut CliHandle<'_, W, E>, RawCommand<'_>) -> Result<(), E>,
67        > {
68            f: F,
69            _ph: PhantomData<(W, E)>,
70        }
71
72        impl<
73                W: Write<Error = E>,
74                E: embedded_io::Error,
75                F: FnMut(&mut CliHandle<'_, W, E>, RawCommand<'_>) -> Result<(), E>,
76            > CommandProcessor<W, E> for Processor<W, E, F>
77        {
78            fn process<'a>(
79                &mut self,
80                cli: &mut CliHandle<'_, W, E>,
81                raw: RawCommand<'a>,
82            ) -> Result<(), ProcessError<'a, E>> {
83                (self.f)(cli, raw)?;
84                Ok(())
85            }
86        }
87
88        Processor {
89            f,
90            _ph: PhantomData,
91        }
92    }
93}
94
95impl<'a> Autocomplete for RawCommand<'a> {
96    #[cfg(feature = "autocomplete")]
97    fn autocomplete(_: Request<'_>, _: &mut Autocompletion<'_>) {
98        // noop
99    }
100}
101
102impl<'a> Help for RawCommand<'a> {
103    #[cfg(feature = "help")]
104    fn command_count() -> usize {
105        0
106    }
107
108    #[cfg(feature = "help")]
109    fn list_commands<W: Write<Error = E>, E: embedded_io::Error>(
110        _: &mut crate::writer::Writer<'_, W, E>,
111    ) -> Result<(), E> {
112        // noop
113        Ok(())
114    }
115
116    #[cfg(feature = "help")]
117    fn command_help<
118        W: Write<Error = E>,
119        E: embedded_io::Error,
120        F: FnMut(&mut crate::writer::Writer<'_, W, E>) -> Result<(), E>,
121    >(
122        _: &mut F,
123        _: RawCommand<'_>,
124        _: &mut crate::writer::Writer<'_, W, E>,
125    ) -> Result<(), HelpError<E>> {
126        // noop
127        Err(HelpError::UnknownCommand)
128    }
129}
130
131impl<'a> FromRaw<'a> for RawCommand<'a> {
132    fn parse(raw: RawCommand<'a>) -> Result<Self, ParseError<'a>> {
133        Ok(raw)
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use rstest::rstest;
140
141    use crate::{arguments::ArgList, command::RawCommand, token::Tokens};
142
143    #[rstest]
144    #[case("set led 1", "set", "led 1")]
145    #[case("  get   led   2  ", "get", "led   2")]
146    #[case("get", "get", "")]
147    #[case("set led 1", "set", "led 1")]
148    fn parsing_some(#[case] input: &str, #[case] name: &str, #[case] args: &str) {
149        let mut input = input.as_bytes().to_vec();
150        let input = core::str::from_utf8_mut(&mut input).unwrap();
151        let input_tokens = Tokens::new(input);
152        let mut args = args.as_bytes().to_vec();
153        let args = core::str::from_utf8_mut(&mut args).unwrap();
154        let arg_tokens = Tokens::new(args);
155
156        assert_eq!(
157            RawCommand::from_tokens(&input_tokens).unwrap(),
158            RawCommand {
159                name: name,
160                args: ArgList::new(arg_tokens)
161            }
162        );
163    }
164
165    #[rstest]
166    #[case("   ")]
167    #[case("")]
168    fn parsing_none(#[case] input: &str) {
169        let mut input = input.as_bytes().to_vec();
170        let input = core::str::from_utf8_mut(&mut input).unwrap();
171        let tokens = Tokens::new(input);
172
173        assert!(RawCommand::from_tokens(&tokens).is_none());
174    }
175}