1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use embedded_io::Write;

use crate::{arguments::FromArgumentError, cli::CliHandle, command::RawCommand};

#[cfg(feature = "autocomplete")]
use crate::autocomplete::{Autocompletion, Request};

#[cfg(feature = "help")]
use crate::writer::Writer;

#[derive(Debug)]
pub enum ProcessError<'a, E: embedded_io::Error> {
    ParseError(ParseError<'a>),
    WriteError(E),
}

#[derive(Debug)]
#[non_exhaustive]
pub enum ParseError<'a> {
    MissingRequiredArgument {
        /// Name of the argument. For example `<FILE>`, `-f <FILE>`, `--file <FILE>`
        name: &'a str,
    },

    NonAsciiShortOption,

    ParseValueError {
        value: &'a str,
        expected: &'static str,
    },

    UnexpectedArgument {
        value: &'a str,
    },

    UnexpectedLongOption {
        name: &'a str,
    },

    UnexpectedShortOption {
        name: char,
    },

    UnknownCommand,
}

impl<'a> From<FromArgumentError<'a>> for ParseError<'a> {
    fn from(error: FromArgumentError<'a>) -> Self {
        Self::ParseValueError {
            value: error.value,
            expected: error.expected,
        }
    }
}

impl<'a, E: embedded_io::Error> From<E> for ProcessError<'a, E> {
    fn from(value: E) -> Self {
        Self::WriteError(value)
    }
}

impl<'a, E: embedded_io::Error> From<ParseError<'a>> for ProcessError<'a, E> {
    fn from(value: ParseError<'a>) -> Self {
        Self::ParseError(value)
    }
}

#[derive(Debug)]
pub enum HelpError<E: embedded_io::Error> {
    WriteError(E),
    UnknownCommand,
}

impl<E: embedded_io::Error> From<E> for HelpError<E> {
    fn from(value: E) -> Self {
        Self::WriteError(value)
    }
}

pub trait Autocomplete {
    // trait is kept available so it's possible to use same where clause
    #[cfg(feature = "autocomplete")]
    /// Try to process autocompletion request
    /// Autocompleted bytes (not present in request) should be written to
    /// given autocompletion.
    fn autocomplete(request: Request<'_>, autocompletion: &mut Autocompletion<'_>);
}

// trait is kept available so it's possible to use same where clause
pub trait Help {
    #[cfg(feature = "help")]
    /// How many commands are known
    fn command_count() -> usize;

    #[cfg(feature = "help")]
    /// Print all commands and short description of each
    fn list_commands<W: Write<Error = E>, E: embedded_io::Error>(
        writer: &mut Writer<'_, W, E>,
    ) -> Result<(), E>;

    #[cfg(feature = "help")]
    /// Print help for given command. Command might contain -h or --help options
    /// Use given writer to print help text
    /// If help request cannot be processed by this object,
    /// Err(HelpError::UnknownCommand) must be returned
    fn command_help<
        W: Write<Error = E>,
        E: embedded_io::Error,
        F: FnMut(&mut Writer<'_, W, E>) -> Result<(), E>,
    >(
        parent: &mut F,
        command: RawCommand<'_>,
        writer: &mut Writer<'_, W, E>,
    ) -> Result<(), HelpError<E>>;
}

pub trait FromRaw<'a>: Sized {
    /// Parse raw command into typed command
    fn parse(raw: RawCommand<'a>) -> Result<Self, ParseError<'a>>;
}

pub trait CommandProcessor<W: Write<Error = E>, E: embedded_io::Error> {
    fn process<'a>(
        &mut self,
        cli: &mut CliHandle<'_, W, E>,
        raw: RawCommand<'a>,
    ) -> Result<(), ProcessError<'a, E>>;
}

impl<W, E, F> CommandProcessor<W, E> for F
where
    W: Write<Error = E>,
    E: embedded_io::Error,
    F: for<'a> FnMut(&mut CliHandle<'_, W, E>, RawCommand<'a>) -> Result<(), ProcessError<'a, E>>,
{
    fn process<'a>(
        &mut self,
        cli: &mut CliHandle<'_, W, E>,
        command: RawCommand<'a>,
    ) -> Result<(), ProcessError<'a, E>> {
        self(cli, command)
    }
}