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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use clap::{Parser, Subcommand};
use std::{
    fmt::{self, Display},
    path::PathBuf,
};

use calypso_base::ui::{self, atty::Stream, termcolor::ColorChoice};

#[derive(Debug, Parser)]
pub struct Args {
    /// Set how color is displayed, if at all. By default this is set to
    /// `auto`.
    ///
    /// Possible values:
    ///
    /// - `always`: Always use color, even if stdout/stderr is not a TTY
    ///
    /// - `ansi`: Always use color, using ANSI escape codes, even if
    /// stdout/stderr is not a TTY or does not support them.
    ///
    /// - `auto`: Use color if stdout/stderr is a TTY, don't if it is not.
    ///
    /// - `never`: Never use color, even if stdout/stderr is a TTY.
    #[clap(
        long,
        parse(from_str = parse_color_prefs),
        possible_values = &[
            "always",
            "ansi",
            "auto",
            "never"
        ],
        default_value = "auto"
    )]
    pub color: (ColorChoice, ColorChoice),

    /// The logging filter to use.
    ///
    /// See tracing-subscriber's EnvFilter type for an explanation of the format:
    ///
    /// https://docs.rs/tracing-subscriber/*/tracing_subscriber/filter/struct.EnvFilter.html
    #[clap(long, env = "CALYPSO_LOG")]
    pub log: Option<String>,

    /// The logging format to use.
    ///
    /// Formats available:
    ///
    /// - `pretty`: Overly verbose, but good for human consumption.
    ///
    /// - `compact`: Less verbose, but still readable.
    ///
    /// - `json`: Intended for machine interpretation.
    /// (see https://docs.rs/tracing-subscriber/*/tracing_subscriber/fmt/format/struct.Json.html)
    #[clap(
        long,
        env = "CALYPSO_LOG_FORMAT",
        possible_values = &[
            "pretty",
            "compact",
            "json"
        ],
        default_value = "compact",
        parse(from_str = parse_log_format)
    )]
    pub log_format: LogFormat,

    #[clap(subcommand)]
    pub cmd: Command,
}

#[derive(Debug, Subcommand)]
pub enum Command {
    /// Explain an error that has detailed information on troubleshooting.
    #[clap(visible_aliases = &["expl", "exp", "ex"])]
    Explain {
        /// The error to get information for. This must be the error code of
        /// the error, which is of the form `EXXXX` (e.g. E0591).
        #[clap(name = "EXXXX")]
        ecode: String,
    },
    /// Commands used for debugging Calypso's internals and implementation.
    #[clap(visible_alias = "int")]
    Internal {
        #[clap(subcommand)]
        cmd: InternalCmd,
    },
}

#[derive(Debug, Subcommand)]
pub enum InternalCmd {
    /// Show information about the environment where this executable was built.
    #[clap(visible_aliases = &["bi", "buildinfo"])]
    BuildInfo,
    /// Intentionally panic in order to test out handling of ICEs (internal
    /// compiler errors).
    Panic,
    /// Run analyses on Calypso source files and return the result in an
    /// "unpretty" format, e.g. AST (abstract syntax tree) or token list.
    #[clap(visible_alias = "up")]
    Unpretty {
        /// Use a REPL-like interface when using standard input. This does not
        /// affect behaviour when using file input.
        #[clap(short, long)]
        repl: bool,
        /// The "unpretty" format to output.
        ///
        /// Current formats:
        ///
        /// - `toks`: Token list
        ///
        /// - `ast`: Abstract syntax tree (AST)
        #[clap(possible_values = &[
            "toks",
            "ast"
        ], parse(from_str = parse_unpretty))]
        format: UnprettyFormat,
        /// The input file to run transformations on. Use the file name `-`
        /// (without backticks) to use standard input.
        #[clap(parse(from_os_str))]
        input: PathBuf,
    },
}

#[derive(Copy, Clone, Debug)]
pub enum UnprettyFormat {
    TokenList,
    Ast,
}

impl Display for UnprettyFormat {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            UnprettyFormat::Ast => write!(f, "ast"),
            UnprettyFormat::TokenList => write!(f, "toks"),
        }
    }
}

#[derive(Copy, Clone, Debug)]
pub enum LogFormat {
    Pretty,
    Compat,
    Json,
}

fn parse_color_prefs(s: &str) -> (ColorChoice, ColorChoice) {
    (
        ui::parse_color_pref(s, Stream::Stdout),
        ui::parse_color_pref(s, Stream::Stderr),
    )
}

fn parse_unpretty(s: &str) -> UnprettyFormat {
    match s {
        "toks" => UnprettyFormat::TokenList,
        "ast" => UnprettyFormat::Ast,
        _ => unreachable!(),
    }
}

fn parse_log_format(s: &str) -> LogFormat {
    match s {
        "pretty" => LogFormat::Pretty,
        "compact" => LogFormat::Compat,
        "json" => LogFormat::Json,
        _ => unreachable!(),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn verify_app() {
        use clap::IntoApp;
        Args::into_app().debug_assert();
    }
}