use clap::{Parser, ValueEnum};
use std::{env, io::IsTerminal, process};
use tectonic_status_base::plain::PlainStatusBackend;
use tectonic::{
config::PersistentConfig,
errors::SyncError,
status::{
termcolor::TermcolorStatusBackend,
{ChatterLevel, StatusBackend},
},
unstable_opts,
};
mod compile;
#[cfg(feature = "serialization")]
mod v2cli;
#[cfg(not(feature = "serialization"))]
mod v2cli {
use std::{ffi::OsString, process};
pub fn v2_main(_effective_args: &[OsString]) {
eprintln!(
"fatal error: the \"V2\" Tectonic CLI requires the code to have been built \
with the \"serialization\" Cargo feature active. This one wasn't."
);
process::exit(1);
}
}
#[derive(Debug, Parser)]
#[command(name = "Tectonic", version, about = "Process a (La)TeX document")]
struct CliOptions {
#[arg(short = 'X')]
use_v2: bool,
#[arg(long = "chatter", short, default_value = "default")]
chatter_level: ChatterLevel,
#[arg(long = "color", default_value = "auto")]
cli_color: CliColor,
#[command(flatten)]
compile: compile::CompileOptions,
}
#[derive(ValueEnum, Clone, Debug)]
enum CliColor {
#[value(name = "always")]
Always,
#[value(name = "auto")]
Auto,
#[value(name = "never")]
Never,
}
impl CliColor {
pub fn should_enable(&self) -> bool {
match self {
Self::Always => true,
Self::Auto => std::io::stdout().is_terminal(),
Self::Never => false,
}
}
}
#[derive(Parser)]
struct PeekUnstableOptions {
#[arg(name = "option", short = 'Z')]
unstable: Vec<unstable_opts::UnstableArg>,
#[arg()]
_remainder: Vec<std::ffi::OsString>,
}
fn main() {
let os_args: Vec<_> = env::args_os().collect();
if let Ok(args) = PeekUnstableOptions::try_parse() {
unstable_opts::UnstableOptions::from_unstable_args(args.unstable.into_iter());
}
let mut v2cli_enabled = false;
let mut v2cli_args = os_args[1..].to_vec();
if !os_args.is_empty() && os_args[0].to_str().map(|s| s.contains("nextonic")) == Some(true) {
v2cli_enabled = true;
} else if let Some(index) = v2cli_args
.to_vec()
.iter()
.position(|s| s.to_str().unwrap_or_default() == "-X")
{
if CliOptions::try_parse().is_err() || CliOptions::parse().use_v2 {
v2cli_enabled = true;
v2cli_args.remove(index);
}
}
if v2cli_enabled {
v2cli::v2_main(&v2cli_args);
return;
}
let args = CliOptions::parse();
tectonic::test_util::maybe_activate_test_mode();
let config = match PersistentConfig::open(false) {
Ok(c) => c,
Err(ref e) => {
e.dump_uncolorized();
process::exit(1);
}
};
let mut status = if args.cli_color.should_enable() {
Box::new(TermcolorStatusBackend::new(args.chatter_level)) as Box<dyn StatusBackend>
} else {
Box::new(PlainStatusBackend::new(args.chatter_level)) as Box<dyn StatusBackend>
};
if let Err(e) = args.compile.execute(config, &mut *status) {
status.report_error(&SyncError::new(e).into());
process::exit(1)
}
}