camxes-rs 1.1.1

Lojban PEG parser with semantic analysis - integrated camxes parser and tersmu semantic engine
Documentation
//! CLI options matching [Main.hs](../Main.hs) (`System.Console.GetOpt`).

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OutputType {
    #[default]
    Both,
    Jbo,
    Loj,
    Prolog,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum InputType {
    #[default]
    WholeText,
    Lines,
    Paras,
}

#[derive(Debug, Clone, Default)]
pub struct Options {
    pub output: OutputType,
    pub input: InputType,
    pub utf8: bool,
    pub json: bool,
    pub indicator_texticules: bool,
    /// Enable egglog equality-saturation analysis (requires `egglog` feature).
    pub egglog: bool,
}

pub fn print_version() {
    println!("1.0.0");
}

pub fn print_help() {
    eprintln!(
        "Usage: camxes [OPTION...] [in] [out]\n\
         \t(use '-' for stdin/stdout)\n\
         Options:\n\
         \t-l  --loj      output logical form only\n\
         \t-j  --jbo      output forethoughtful lojbanic form only\n\
         \t-P  --prolog   output Prolog (SWI-Prolog) source\n\
         \t-L  --lines    interpret each line as a lojban text\n\
         \t-p  --paragraphs  interpret each blank-line-separated paragraph as a lojban text\n\
         \t-u  --utf8     output utf8 encoded text rather than ascii\n\
         \t-v  --version  show version\n\
         \t-h  --help     show help\n\
         \t    --json     output one JSON object per line (NDJSON)\n\
         \t    --indicator-texticules  turn supported attitudinals into side texticules\n\
         \t-E  --egglog   run egglog equality-saturation analysis (adds egglog_graph to JSON)\n"
    );
}

/// Parse argv like Main.hs: last repeated flag wins for output/input mode.
pub fn parse_args(args: &[String]) -> Result<(Options, Vec<String>), String> {
    let mut opts = Options::default();
    let mut rest = Vec::new();
    let mut i = 0;
    while i < args.len() {
        let a = &args[i];
        match a.as_str() {
            "-h" | "--help" => {
                print_help();
                std::process::exit(0);
            }
            "-v" | "--version" => {
                print_version();
                std::process::exit(0);
            }
            "-l" | "--loj" => opts.output = OutputType::Loj,
            "-j" | "--jbo" => opts.output = OutputType::Jbo,
            "-P" | "--prolog" => opts.output = OutputType::Prolog,
            "-L" | "--lines" => opts.input = InputType::Lines,
            "-p" | "--paragraphs" => opts.input = InputType::Paras,
            "-u" | "--utf8" => opts.utf8 = true,
            "--json" => opts.json = true,
            "--indicator-texticules" => opts.indicator_texticules = true,
            "-E" | "--egglog" => opts.egglog = true,
            // Match Haskell GetOpt: bare `-` is stdin/stdout, not a flag (see `examples/tersmuLines`).
            "-" => rest.push(a.clone()),
            s if s.starts_with('-') => {
                return Err(format!("unknown option {s}\nRun with --help for usage."));
            }
            _ => rest.push(a.clone()),
        }
        i += 1;
    }
    Ok((opts, rest))
}

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

    #[test]
    fn bare_dash_is_stdin_not_unknown_option() {
        let (_opts, rest) = parse_args(&["-".to_string()]).expect("parse");
        assert_eq!(rest, vec!["-".to_string()]);
    }

    #[test]
    fn indicator_texticules_flag_is_opt_in() {
        let (opts, rest) = parse_args(&["--indicator-texticules".to_string(), "in.loj".to_string()]).expect("parse");
        assert!(opts.indicator_texticules);
        assert_eq!(rest, vec!["in.loj".to_string()]);
    }
}