lexigram 0.9.3

Lexer and LL(1) parser generator
// Copyright (c) 2025 Redglyph (@gmail.com). All Rights Reserved.

#![doc = include_str!("../README.md")]

use lexi_gram::gen_parser::{try_gen_parser, GenParserError};
use lexi_gram::{lexigram_lib, LEXIGRAM_PKG_VERSION, LEXIGRAM_PKG_NAME};
use lexi_gram::lexigram_lib::CollectJoin;
use lexigram_lib::{LIB_PKG_VERSION, LIB_PKG_NAME};
use lexigram_lib::lexigram_core::{CORE_PKG_VERSION, CORE_PKG_NAME};
use lexigram_lib::log::LogStatus;
use crate::arg_opt::{parse_args, ArgOptions, HELP_MESSAGE};

mod arg_opt;

// package name & version
pub const BIN_PKG_NAME: &str = env!("CARGO_PKG_NAME");
pub const BIN_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");

/// Error returned by [execute].
pub enum ExeError {
    /// The "help" option was met; the [help message](HELP_MESSAGE) should be displayed.
    Help,
    /// The "version" option was met; the versions of the executable and the other
    /// crates it depends on should be displayed.
    Version,
    /// An unknown command-line option was given.
    Option(String),
    /// The source code generator produced an error. The original error is
    /// in the `source` field.
    ///
    /// The `show_log` field determines whether the "log" option was desired or not.
    GenParser { source: GenParserError, show_log: bool },
    /// A general message should be displayed.
    Message(String),
}

/// ANSI colour option for Lexigram.
///
/// Default: [ExeAnsi::On]
#[derive(Clone, Copy, Default, PartialEq, Debug)]
pub enum ExeAnsi {
    /// Don't output ANSI codes
    Off,
    /// Outputs ANSI codes; enables ANSI output on Windows console
    #[default] On,
    /// Outputs ANSI codes. Doesn't try to enable ANSI output on Windows console: use this
    /// option if [ExeAnsi::On] generates problems.
    OnPassive,
}

fn main() {
    let all_args: Vec<String> = std::env::args().skip(1).collect();
    let code = match execute(all_args) {
        Ok(message_opt) => {
            if let Some(message) = message_opt {
                println!("{message}");
            }
            eprintln!("OK");
            0
        }
        Err(e) => {
            match e {
                ExeError::Help => {
                    eprintln!("{HELP_MESSAGE}");
                    1
                }
                ExeError::Version => {
                    eprintln!("Versions:\n- {BIN_PKG_NAME}: {BIN_PKG_VERSION}");
                    eprintln!("- {CORE_PKG_NAME}: {CORE_PKG_VERSION}");
                    eprintln!("- {LIB_PKG_NAME}: {LIB_PKG_VERSION}");
                    eprintln!("- {LEXIGRAM_PKG_NAME}: {LEXIGRAM_PKG_VERSION}");
                    1
                }
                ExeError::Option(msg) => {
                    eprintln!("Error while parsing the command arguments: {msg}");
                    2
                }
                ExeError::GenParser { source, .. } => {
                    eprintln!("Generator error:\n{source}");
                    3
                }
                ExeError::Message(s) => {
                    eprintln!("{s}");
                    4
                }
            }
        }
    };
    std::process::exit(code);
}

/// Parses the command-line arguments in `all_args` and executes the corresponding actions.
pub fn execute(all_args: Vec<String>) -> Result<Option<String>, ExeError> {
    let (action, arg_options) = parse_args(all_args)?;
    let ArgOptions { gen_options, show_log, ansi } = arg_options;
    if ansi == ExeAnsi::On {
        // enables ANSI support on Windows console
        match enable_ansi_support::enable_ansi_support() {
            Ok(()) => {}
            Err(e) => return Err(ExeError::Message(format!("ERROR: this console doesn't support ANSI output:\n{e}"))),
        }
    }
    match try_gen_parser(action, gen_options) {
        Ok(log) => {
            Ok(if show_log {
                Some(log.to_string())
            } else {
                Some(format!("{}{}", log.get_warnings().map(|s| s.to_string()).join("\n"), log.get_totals()))
            })
        }
        Err(source) => {
            Err(ExeError::GenParser { source, show_log })
        }
    }
}