optional_command/
optional_command.rs

1// This example shows how to implement a command with a "catch all."
2//
3// This requires writing your own impl for `Decodable` because docopt's
4// decoder uses `Option<T>` to mean "T may not be present" rather than
5// "T may be present but incorrect."
6
7use std::fmt;
8
9use qsv_docopt::Docopt;
10use serde::{
11    Deserialize,
12    de::{Deserializer, Error, Visitor},
13};
14
15// Write the Docopt usage string.
16const USAGE: &str = "
17Rust's package manager
18
19Usage:
20    mycli [<command>]
21
22Options:
23    -h, --help       Display this message
24";
25
26#[derive(Debug, Deserialize)]
27struct Args {
28    arg_command: Command,
29}
30
31struct CommandVisitor;
32
33impl<'de> Visitor<'de> for CommandVisitor {
34    type Value = Command;
35
36    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
37        formatter.write_str("a string A, B or C")
38    }
39
40    fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
41    where
42        E: Error,
43    {
44        Ok(match s {
45            "" => Command::None,
46            "A" => Command::A,
47            "B" => Command::B,
48            "C" => Command::C,
49            s => Command::Unknown(s.to_string()),
50        })
51    }
52}
53
54impl<'de> Deserialize<'de> for Command {
55    fn deserialize<D>(d: D) -> Result<Command, D::Error>
56    where
57        D: Deserializer<'de>,
58    {
59        d.deserialize_str(CommandVisitor)
60    }
61}
62
63#[derive(Debug)]
64enum Command {
65    A,
66    B,
67    C,
68    Unknown(String),
69    None,
70}
71
72fn main() {
73    let args: Args = Docopt::new(USAGE)
74        .and_then(|d| d.deserialize())
75        .unwrap_or_else(|e| e.exit());
76    println!("{:?}", args);
77}