Skip to main content

camxes_rs/
cli.rs

1//! CLI options matching [Main.hs](../Main.hs) (`System.Console.GetOpt`).
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
4pub enum OutputType {
5    #[default]
6    Both,
7    Jbo,
8    Loj,
9    Prolog,
10}
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
13pub enum InputType {
14    #[default]
15    WholeText,
16    Lines,
17    Paras,
18}
19
20#[derive(Debug, Clone, Default)]
21pub struct Options {
22    pub output: OutputType,
23    pub input: InputType,
24    pub utf8: bool,
25    pub json: bool,
26    pub indicator_texticules: bool,
27    /// Enable egglog equality-saturation analysis (requires `egglog` feature).
28    pub egglog: bool,
29}
30
31pub fn print_version() {
32    println!("1.0.0");
33}
34
35pub fn print_help() {
36    eprintln!(
37        "Usage: camxes [OPTION...] [in] [out]\n\
38         \t(use '-' for stdin/stdout)\n\
39         Options:\n\
40         \t-l  --loj      output logical form only\n\
41         \t-j  --jbo      output forethoughtful lojbanic form only\n\
42         \t-P  --prolog   output Prolog (SWI-Prolog) source\n\
43         \t-L  --lines    interpret each line as a lojban text\n\
44         \t-p  --paragraphs  interpret each blank-line-separated paragraph as a lojban text\n\
45         \t-u  --utf8     output utf8 encoded text rather than ascii\n\
46         \t-v  --version  show version\n\
47         \t-h  --help     show help\n\
48         \t    --json     output one JSON object per line (NDJSON)\n\
49         \t    --indicator-texticules  turn supported attitudinals into side texticules\n\
50         \t-E  --egglog   run egglog equality-saturation analysis (adds egglog_graph to JSON)\n"
51    );
52}
53
54/// Parse argv like Main.hs: last repeated flag wins for output/input mode.
55pub fn parse_args(args: &[String]) -> Result<(Options, Vec<String>), String> {
56    let mut opts = Options::default();
57    let mut rest = Vec::new();
58    let mut i = 0;
59    while i < args.len() {
60        let a = &args[i];
61        match a.as_str() {
62            "-h" | "--help" => {
63                print_help();
64                std::process::exit(0);
65            }
66            "-v" | "--version" => {
67                print_version();
68                std::process::exit(0);
69            }
70            "-l" | "--loj" => opts.output = OutputType::Loj,
71            "-j" | "--jbo" => opts.output = OutputType::Jbo,
72            "-P" | "--prolog" => opts.output = OutputType::Prolog,
73            "-L" | "--lines" => opts.input = InputType::Lines,
74            "-p" | "--paragraphs" => opts.input = InputType::Paras,
75            "-u" | "--utf8" => opts.utf8 = true,
76            "--json" => opts.json = true,
77            "--indicator-texticules" => opts.indicator_texticules = true,
78            "-E" | "--egglog" => opts.egglog = true,
79            // Match Haskell GetOpt: bare `-` is stdin/stdout, not a flag (see `examples/tersmuLines`).
80            "-" => rest.push(a.clone()),
81            s if s.starts_with('-') => {
82                return Err(format!("unknown option {s}\nRun with --help for usage."));
83            }
84            _ => rest.push(a.clone()),
85        }
86        i += 1;
87    }
88    Ok((opts, rest))
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn bare_dash_is_stdin_not_unknown_option() {
97        let (_opts, rest) = parse_args(&["-".to_string()]).expect("parse");
98        assert_eq!(rest, vec!["-".to_string()]);
99    }
100
101    #[test]
102    fn indicator_texticules_flag_is_opt_in() {
103        let (opts, rest) = parse_args(&["--indicator-texticules".to_string(), "in.loj".to_string()]).expect("parse");
104        assert!(opts.indicator_texticules);
105        assert_eq!(rest, vec!["in.loj".to_string()]);
106    }
107}