embedded_cli/
service.rs

1use embedded_io::Write;
2
3use crate::{arguments::FromArgumentError, cli::CliHandle, command::RawCommand};
4
5#[cfg(feature = "autocomplete")]
6use crate::autocomplete::{Autocompletion, Request};
7
8#[cfg(feature = "help")]
9use crate::writer::Writer;
10
11#[derive(Debug)]
12pub enum ProcessError<'a, E: embedded_io::Error> {
13    ParseError(ParseError<'a>),
14    WriteError(E),
15}
16
17#[derive(Debug)]
18#[non_exhaustive]
19pub enum ParseError<'a> {
20    MissingRequiredArgument {
21        /// Name of the argument. For example `<FILE>`, `-f <FILE>`, `--file <FILE>`
22        name: &'a str,
23    },
24
25    NonAsciiShortOption,
26
27    ParseValueError {
28        value: &'a str,
29        expected: &'static str,
30    },
31
32    UnexpectedArgument {
33        value: &'a str,
34    },
35
36    UnexpectedLongOption {
37        name: &'a str,
38    },
39
40    UnexpectedShortOption {
41        name: char,
42    },
43
44    UnknownCommand,
45}
46
47impl<'a> From<FromArgumentError<'a>> for ParseError<'a> {
48    fn from(error: FromArgumentError<'a>) -> Self {
49        Self::ParseValueError {
50            value: error.value,
51            expected: error.expected,
52        }
53    }
54}
55
56impl<'a, E: embedded_io::Error> From<E> for ProcessError<'a, E> {
57    fn from(value: E) -> Self {
58        Self::WriteError(value)
59    }
60}
61
62impl<'a, E: embedded_io::Error> From<ParseError<'a>> for ProcessError<'a, E> {
63    fn from(value: ParseError<'a>) -> Self {
64        Self::ParseError(value)
65    }
66}
67
68#[derive(Debug)]
69pub enum HelpError<E: embedded_io::Error> {
70    WriteError(E),
71    UnknownCommand,
72}
73
74impl<E: embedded_io::Error> From<E> for HelpError<E> {
75    fn from(value: E) -> Self {
76        Self::WriteError(value)
77    }
78}
79
80pub trait Autocomplete {
81    // trait is kept available so it's possible to use same where clause
82    #[cfg(feature = "autocomplete")]
83    /// Try to process autocompletion request
84    /// Autocompleted bytes (not present in request) should be written to
85    /// given autocompletion.
86    fn autocomplete(request: Request<'_>, autocompletion: &mut Autocompletion<'_>);
87}
88
89// trait is kept available so it's possible to use same where clause
90pub trait Help {
91    #[cfg(feature = "help")]
92    /// How many commands are known
93    fn command_count() -> usize;
94
95    #[cfg(feature = "help")]
96    /// Print all commands and short description of each
97    fn list_commands<W: Write<Error = E>, E: embedded_io::Error>(
98        writer: &mut Writer<'_, W, E>,
99    ) -> Result<(), E>;
100
101    #[cfg(feature = "help")]
102    /// Print help for given command. Command might contain -h or --help options
103    /// Use given writer to print help text
104    /// If help request cannot be processed by this object,
105    /// Err(HelpError::UnknownCommand) must be returned
106    fn command_help<
107        W: Write<Error = E>,
108        E: embedded_io::Error,
109        F: FnMut(&mut Writer<'_, W, E>) -> Result<(), E>,
110    >(
111        parent: &mut F,
112        command: RawCommand<'_>,
113        writer: &mut Writer<'_, W, E>,
114    ) -> Result<(), HelpError<E>>;
115}
116
117pub trait FromRaw<'a>: Sized {
118    /// Parse raw command into typed command
119    fn parse(raw: RawCommand<'a>) -> Result<Self, ParseError<'a>>;
120}
121
122pub trait CommandProcessor<W: Write<Error = E>, E: embedded_io::Error> {
123    fn process<'a>(
124        &mut self,
125        cli: &mut CliHandle<'_, W, E>,
126        raw: RawCommand<'a>,
127    ) -> Result<(), ProcessError<'a, E>>;
128}
129
130impl<W, E, F> CommandProcessor<W, E> for F
131where
132    W: Write<Error = E>,
133    E: embedded_io::Error,
134    F: for<'a> FnMut(&mut CliHandle<'_, W, E>, RawCommand<'a>) -> Result<(), ProcessError<'a, E>>,
135{
136    fn process<'a>(
137        &mut self,
138        cli: &mut CliHandle<'_, W, E>,
139        command: RawCommand<'a>,
140    ) -> Result<(), ProcessError<'a, E>> {
141        self(cli, command)
142    }
143}