type_cli/
lib.rs

1use std::error::Error as StdError;
2
3pub use type_cli_derive::CLI;
4
5mod args;
6pub use args::{Argument, Flag, OptionalArg};
7
8pub trait CLI: Sized {
9    ///
10    /// Parses the arguments as a command-line interface of the current type,
11    /// returning errors as a value for manul handling.
12    ///
13    /// If you don't need fine control over program flow, use `CLI::processs` instead.
14    fn parse(args: impl std::iter::Iterator<Item = String>) -> Result<Parse<Self>, Error>;
15    ///
16    /// Parses `std::env::args()` as a command-line interface of the current type.
17    ///
18    /// If an error occurs while parsing, it will be send to stderr and the process will exit.
19    /// If the user enters `--help` or `-h`, help info will be sent to stderr and the process will exit.
20    ///
21    /// If you want finer control over program flow, use `CLI::parse` instead.
22    fn process() -> Self {
23        match Self::parse(std::env::args()) {
24            Ok(Parse::Success(val)) => val,
25            Ok(Parse::Help(help)) => {
26                eprintln!("{}", help);
27                std::process::exit(1);
28            }
29            Err(e) => {
30                eprintln!("{}", e);
31                std::process::exit(101);
32            }
33        }
34    }
35}
36
37///
38/// A result of successful command-line interface parsing.
39///
40/// This is either a data structure holding the arguments passed to the program,
41/// or a string containing help info about the current command.
42pub enum Parse<T: CLI> {
43    Success(T),
44    Help(HelpInfo),
45}
46
47pub struct HelpInfo(pub &'static str);
48
49impl std::fmt::Display for HelpInfo {
50    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51        write!(f, "{}", self.0)
52    }
53}
54
55#[derive(thiserror::Error)]
56pub enum Error {
57    #[error("Expected an argument named `{0}`")]
58    ExpectedNamed(&'static str),
59    #[error("Expected an argument at position `{0}`")]
60    ExpectedPositional(usize),
61    #[error("Expected a value after argument `{0}`")]
62    ExpectedValue(&'static str),
63    #[error("Unknown flag `{0}`")]
64    UnknownFlag(String),
65    #[error("Unexpected positional argument `{0}`")]
66    ExtraArg(String),
67    #[error("Unknown subcommand `{0}`")]
68    UnknownSub(String),
69    #[error("Error parsing {0}:\n{1}")]
70    Parse(ArgRef, Box<dyn StdError>),
71}
72
73/// A way to refer to an argument in an error.
74pub enum ArgRef {
75    Positional(usize),
76    Named(&'static str),
77}
78use std::fmt::{self, Display};
79impl Display for ArgRef {
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
81        match self {
82            &ArgRef::Positional(index) => {
83                write!(f, "positional argument `{}`", index)
84            }
85            &ArgRef::Named(name) => {
86                write!(f, "argument `{}`", name)
87            }
88        }
89    }
90}
91
92impl std::fmt::Debug for Error {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        std::fmt::Display::fmt(self, f)
95    }
96}