mod arg_matches;
mod flag;
#[cfg(not(feature = "std"))]
use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::fmt;
pub use self::{
arg_matches::ArgMatches,
flag::{CompileFlag, FlagAction},
};
use crate::diagnostics::Report;
#[derive(Clone)]
pub struct CompileFlags {
flags: Vec<CompileFlag>,
arg_matches: ArgMatches,
}
#[cfg(feature = "std")]
impl Default for CompileFlags {
fn default() -> Self {
Self::new(None::<std::ffi::OsString>).unwrap()
}
}
#[cfg(not(feature = "std"))]
impl Default for CompileFlags {
fn default() -> Self {
Self::new(None::<alloc::string::String>).unwrap()
}
}
impl From<ArgMatches> for CompileFlags {
fn from(arg_matches: ArgMatches) -> Self {
let flags = inventory::iter::<CompileFlag>.into_iter().cloned().collect();
Self { flags, arg_matches }
}
}
impl CompileFlags {
#[cfg(feature = "std")]
pub fn new<I, V>(argv: I) -> Result<Self, Report>
where
I: IntoIterator<Item = V>,
V: Into<std::ffi::OsString> + Clone,
{
use crate::diagnostics::IntoDiagnostic;
let flags = inventory::iter::<CompileFlag>.into_iter().cloned().collect();
fake_compile_command()
.try_get_matches_from(argv)
.into_diagnostic()
.map(|arg_matches| Self { flags, arg_matches })
}
#[cfg(not(feature = "std"))]
pub fn new<I, V>(argv: I) -> Result<Self, Report>
where
I: IntoIterator<Item = V>,
V: Into<Cow<'static, str>> + Clone,
{
use alloc::collections::{BTreeMap, VecDeque};
let argv = argv.into_iter().map(|arg| arg.into()).collect::<VecDeque<_>>();
let flags = inventory::iter::<CompileFlag>
.into_iter()
.map(|flag| (flag.name, flag))
.collect::<BTreeMap<_, _>>();
let arg_matches = ArgMatches::parse(argv, &flags)?;
let this = Self {
flags: flags.values().copied().cloned().collect(),
arg_matches,
};
Ok(this)
}
pub fn flags(&self) -> &[CompileFlag] {
self.flags.as_slice()
}
pub fn get_flag(&self, name: &str) -> bool {
self.arg_matches.get_flag(name)
}
pub fn get_flag_count(&self, name: &str) -> usize {
self.arg_matches.get_count(name) as usize
}
pub fn matches(&self) -> &ArgMatches {
&self.arg_matches
}
}
impl fmt::Debug for CompileFlags {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut map = f.debug_map();
for id in self.arg_matches.ids() {
use clap::parser::ValueSource;
if id.as_str() == "CompilerOptions" {
continue;
}
if matches!(self.arg_matches.value_source(id.as_str()), Some(ValueSource::DefaultValue))
{
continue;
}
map.key(&id.as_str()).value_with(|f| {
let mut list = f.debug_list();
if let Some(occurs) =
self.arg_matches.try_get_raw_occurrences(id.as_str()).expect("expected flag")
{
list.entries(occurs.flatten());
}
list.finish()
});
}
map.finish()
}
#[cfg(not(feature = "std"))]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut map = f.debug_map();
for (name, raw_values) in self.arg_matches.iter() {
map.key(&name)
.value_with(|f| f.debug_list().entries(raw_values.iter()).finish());
}
map.finish()
}
}
#[cfg(feature = "std")]
fn fake_compile_command() -> clap::Command {
let cmd = clap::Command::new("compile")
.no_binary_name(true)
.disable_help_flag(true)
.disable_version_flag(true)
.disable_help_subcommand(true);
register_flags(cmd)
}
#[cfg(feature = "std")]
pub fn register_flags(cmd: clap::Command) -> clap::Command {
inventory::iter::<CompileFlag>.into_iter().fold(cmd, |cmd, flag| {
let arg = clap::Arg::new(flag.name)
.long(flag.long.unwrap_or(flag.name))
.action(clap::ArgAction::from(flag.action));
let arg = if let Some(help) = flag.help {
arg.help(help)
} else {
arg
};
let arg = if let Some(help_heading) = flag.help_heading {
arg.help_heading(help_heading)
} else {
arg
};
let arg = if let Some(short) = flag.short {
arg.short(short)
} else {
arg
};
let arg = if let Some(env) = flag.env {
arg.env(env)
} else {
arg
};
let arg = if let Some(value) = flag.default_missing_value {
arg.default_missing_value(value)
} else {
arg
};
let arg = if let Some(value) = flag.default_value {
arg.default_value(value)
} else {
arg
};
let arg = if let Some(value) = flag.hide {
arg.hide(value)
} else {
arg
};
cmd.arg(arg)
})
}