summaryrefslogtreecommitdiffhomepage
path: root/mullvad-cli/src/cmds/mod.rs
blob: 14a0354137bec1783f0e638274b6d54f653e6e5e (plain)
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
use clap::builder::{PossibleValuesParser, TypedValueParser, ValueParser};
use std::{io::stdin, ops::Deref};

pub mod account;
pub mod anti_censorship;
pub mod api_access;
pub mod auto_connect;
pub mod beta_program;
pub mod custom_list;
pub mod debug;
pub mod dns;
pub mod lan;
pub mod lockdown;
pub mod log;
pub mod patch;
pub mod proxies;
pub mod relay;
pub mod relay_constraints;
pub mod reset;
pub mod split_tunnel;
pub mod status;
pub mod tunnel;
pub mod tunnel_state;
pub mod version;

/// A value parser that parses "on" or "off" into a boolean
#[derive(Debug, Clone, Copy)]
pub struct BooleanOption {
    state: bool,
    on_label: &'static str,
    off_label: &'static str,
}

impl Deref for BooleanOption {
    type Target = bool;

    fn deref(&self) -> &Self::Target {
        &self.state
    }
}

impl clap::builder::ValueParserFactory for BooleanOption {
    type Parser = ValueParser;

    /// A value parser that parses "on" or "off" into a `BooleanOption`
    fn value_parser() -> Self::Parser {
        Self::custom_parser("on", "off")
    }
}

impl BooleanOption {
    /// A value parser that parses `on_label` and `off_label` into a `BooleanOption`
    fn custom_parser(on_label: &'static str, off_label: &'static str) -> ValueParser {
        assert!(on_label != off_label);

        ValueParser::new(
            PossibleValuesParser::new([on_label, off_label])
                .map(move |val| Self::with_labels(val == on_label, on_label, off_label)),
        )
    }

    fn with_labels(state: bool, on_label: &'static str, off_label: &'static str) -> Self {
        Self {
            state,
            on_label,
            off_label,
        }
    }
}

impl From<bool> for BooleanOption {
    fn from(state: bool) -> Self {
        Self::with_labels(state, "on", "off")
    }
}

impl std::fmt::Display for BooleanOption {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if self.state {
            self.on_label.fmt(f)
        } else {
            self.off_label.fmt(f)
        }
    }
}

async fn receive_confirmation(msg: &'static str, default: bool) -> bool {
    let helper_str = match default {
        true => "[Y/n]",
        false => "[y/N]",
    };

    println!("{msg} {helper_str}");

    tokio::task::spawn_blocking(move || {
        loop {
            let mut buf = String::new();
            if let Err(e) = stdin().read_line(&mut buf) {
                eprintln!("Couldn't read from STDIN: {e}");
                return false;
            }
            match buf.trim().to_ascii_lowercase().as_str() {
                "" => return default,
                "y" | "ye" | "yes" => return true,
                "n" | "no" => return false,
                _ => eprintln!("Unexpected response. Please enter \"y\" or \"n\""),
            }
        }
    })
    .await
    .unwrap()
}