use std::default::Default;
use std::path::PathBuf;
use std::str::FromStr;
const HELPMSG: &str = r#"Available unstable options:
-Z help List all unstable options
-Z continue-on-errors Keep compiling even when severe errors occur
-Z min-crossrefs=<num> Equivalent to bibtex's -min-crossrefs flag - "include after <num>
crossrefs" [default: 2]
-Z paper-size=<spec> Change the initial paper size [default: letter]
-Z search-path=<path> Also look in <path> for files (unless --untrusted has been specified),
like TEXINPUTS. Can be specified multiple times.
-Z shell-escape Enable \write18 (unless --untrusted has been specified)
-Z shell-escape-cwd=<path> Working directory to use for \write18. Use $(pwd) for same behaviour as
most other engines (e.g. for relative paths in \inputminted).
Implies -Z shell-escape
-Z deterministic-mode Force a deterministic build environment. Note that setting
`SOURCE_DATE_EPOCH` is usually sufficient for reproducible builds,
and this option makes some extra functionality trade-offs.
Specifically, deterministic mode breaks SyncTeX's auxiliary files
as they include and rely on absolute file paths
"#;
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum UnstableArg {
ContinueOnErrors,
Help,
MinCrossrefs(u32),
PaperSize(String),
SearchPath(PathBuf),
ShellEscapeEnabled,
ShellEscapeCwd(String),
DeterministicModeEnabled,
}
impl FromStr for UnstableArg {
type Err = Box<dyn std::error::Error + Send + Sync + 'static>;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let mut splitter = s.splitn(2, '=');
let arg = splitter.next().unwrap(); let value = splitter.next();
let require_value = |value_name| {
value.ok_or_else(|| {
format!("'-Z {arg}=<{value_name}>' requires a value but none was supplied",).into()
})
};
let require_no_value = |unwanted_value: Option<&str>, builtin_value: UnstableArg| {
if let Some(value) = unwanted_value {
Err(format!(
"'-Z {arg}={value}', was supplied but '-Z {arg}' does not take a value."
)
.into())
} else {
Ok(builtin_value)
}
};
match arg {
"help" => Ok(UnstableArg::Help),
"continue-on-errors" => Ok(UnstableArg::ContinueOnErrors),
"min-crossrefs" => require_value("num")
.and_then(|s| {
FromStr::from_str(s).map_err(|e| format!("-Z min-crossrefs: {e}").into())
})
.map(UnstableArg::MinCrossrefs),
"paper-size" => require_value("spec").map(|s| UnstableArg::PaperSize(s.to_string())),
"search-path" => require_value("path").map(|s| UnstableArg::SearchPath(s.into())),
"shell-escape" => require_no_value(value, UnstableArg::ShellEscapeEnabled),
"shell-escape-cwd" => {
require_value("path").map(|s| UnstableArg::ShellEscapeCwd(s.to_string()))
}
"deterministic-mode" => require_no_value(value, UnstableArg::DeterministicModeEnabled),
_ => Err(format!("Unknown unstable option '{arg}'").into()),
}
}
}
#[derive(Debug, Default)]
pub struct UnstableOptions {
pub continue_on_errors: bool,
pub paper_size: Option<String>,
pub shell_escape: bool,
pub min_crossrefs: Option<u32>,
pub extra_search_paths: Vec<PathBuf>,
pub shell_escape_cwd: Option<String>,
pub deterministic_mode: bool,
}
impl UnstableOptions {
#[doc(hidden)]
pub fn from_unstable_args<I>(uargs: I) -> Self
where
I: Iterator<Item = UnstableArg>,
{
let mut opts = UnstableOptions::default();
for u in uargs {
use UnstableArg::*;
match u {
Help => print_unstable_help_and_exit(),
ContinueOnErrors => opts.continue_on_errors = true,
MinCrossrefs(num) => opts.min_crossrefs = Some(num),
PaperSize(size) => opts.paper_size = Some(size),
ShellEscapeEnabled => opts.shell_escape = true,
SearchPath(p) => opts.extra_search_paths.push(p),
ShellEscapeCwd(p) => {
opts.shell_escape_cwd = Some(p);
opts.shell_escape = true;
}
DeterministicModeEnabled => opts.deterministic_mode = true,
}
}
opts
}
}
#[doc(hidden)]
pub fn print_unstable_help_and_exit() {
print!("{HELPMSG}");
std::process::exit(0);
}