pub mod utils;
mod version;
pub use version::{intercept_version, VersionFormat};
use std::path::PathBuf;
use build_info::VERSION;
use clap::{Args, Command, FromArgMatches, Parser, Subcommand, ValueEnum};
#[derive(Debug, Parser)]
#[clap(name = "mitex-cli", version = VERSION)]
pub struct Opts {
#[arg(short = 'V', long, group = "version-dump")]
pub version: bool,
#[arg(long = "VV", alias = "version-fmt", group = "version-dump", default_value_t = VersionFormat::None)]
pub vv: VersionFormat,
#[clap(subcommand)]
pub sub: Option<Subcommands>,
}
#[derive(ValueEnum, Debug, Clone, PartialEq, Eq)]
#[value(rename_all = "kebab-case")]
pub enum CompileStage {
Syntax,
Document,
}
#[derive(Default, Debug, Clone, Parser)]
#[clap(next_help_heading = "Compile options")]
pub struct CompileArgs {
#[clap(long, short, default_value = ".")]
pub workspace: String,
#[clap(long, short, default_value = "")]
pub input: String,
#[clap(long, value_enum)]
pub stage: Option<CompileStage>,
#[clap(long, short, default_value = "")]
pub output: String,
#[arg(trailing_var_arg = true, hide = true)]
_i_or_o_args: Vec<String>,
}
#[derive(Debug, Subcommand)]
#[clap(about = "The CLI for MiTeX.", next_display_order = None)]
#[allow(clippy::large_enum_variant)]
pub enum Subcommands {
#[clap(visible_alias = "c")]
Compile(CompileArgs),
Completion(CompletionArgs),
Manual(ManualArgs),
#[clap(subcommand)]
Spec(SpecSubCommands),
}
#[derive(Debug, Clone, Parser)]
pub struct CompletionArgs {
#[clap(value_enum)]
pub shell: clap_complete::Shell,
}
#[derive(Debug, Clone, Parser)]
pub struct ManualArgs {
pub dest: PathBuf,
}
#[derive(Debug, Subcommand)]
#[clap(next_display_order = None)]
#[allow(clippy::large_enum_variant)]
pub enum SpecSubCommands {
Generate(GenerateSpecArgs),
}
#[derive(Debug, Clone, Parser)]
pub struct GenerateSpecArgs {}
pub mod build_info {
pub static VERSION: &str = env!("CARGO_PKG_VERSION");
}
pub fn get_cli(sub_command_required: bool) -> Command {
let cli = Command::new("$").disable_version_flag(true);
Opts::augment_args(cli).subcommand_required(sub_command_required)
}
fn process_opts(mut opts: Opts) -> Result<Opts, clap::Error> {
if let Some(Subcommands::Compile(args)) = &mut opts.sub {
let io_args = std::mem::take(&mut args._i_or_o_args);
if args.input.is_empty() && args.output.is_empty() {
match io_args.len() {
0 => {}
1 => {
args.input.clone_from(&io_args[0]);
}
2 => {
args.input.clone_from(&io_args[0]);
args.output.clone_from(&io_args[1]);
}
_ => Err(clap::Error::raw(
clap::error::ErrorKind::ValueValidation,
"Too many positional arguments.",
))?,
}
} else if !io_args.is_empty() {
Err(clap::Error::raw(
clap::error::ErrorKind::ValueValidation,
"Input and output file cannot be positional arguments\
if any of them is specified by named argument.",
))?;
}
if args.input.is_empty() {
Err(clap::Error::raw(
clap::error::ErrorKind::ValueValidation,
"Input file is required.",
))?;
}
if args.output.is_empty() {
std::path::Path::new(&args.input)
.with_extension("tex")
.to_str()
.ok_or_else(|| {
clap::Error::raw(
clap::error::ErrorKind::ValueValidation,
"Input file name is invalid.",
)
})?
.clone_into(&mut args.output);
}
}
Ok(opts)
}
pub fn get_os_opts(sub_command_required: bool) -> Result<Opts, clap::Error> {
let res = Opts::from_arg_matches(&get_cli(sub_command_required).get_matches())?;
process_opts(res)
}