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: &'a str,
24
25 args: ArgList<'a>,
29}
30
31impl<'a> RawCommand<'a> {
32 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 }
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 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 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}