You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

249 lines
9.2 KiB

use super::*;
struct Opt(&'static [&'static str], Option<&'static str>, &'static str, usize, Option<&'static str>, Option<&'static [&'static str]>);
impl Opt
{
const fn with_def(self, vl: &'static str) -> Self
{
Self(self.0, self.1, self.2, self.3, Some(vl), self.5)
}
const fn with_slice(self, vl: &'static [&'static str]) -> Self
{
Self(self.0, self.1, self.2, self.3, self.4, Some(vl))
}
}
/// Macro for printing the options
///
/// # Usage
/// Any combination of the following:
/// ```
/// opt!("--long", "--other"; "Description"); // --long, --other: Description
/// opt!("--long", "--other"; * 2 "Description"); // --long, --other: Description
/// opt!("--long", "--other" => "value name"; "Description"); // --long, --other <value name>: Description
/// opt!("--long"; "Description", ""); // --long, --other: Description (default)
/// opt!("--long"; "Description", "Value"); // --long, --other: Description (default: `Value`)
/// opt!("--long"; "Description", ["OTHER", "OTHER 2"], "value"); // --long, --other: Description (see `OTHER`, `OTHER 2`) (default: `value`)
/// ```
#[macro_export] macro_rules! opt {
(where $desc:literal $(, [$($see:literal),*])?) => {
{
let o = Opt(&[], None, $desc, 2, None, None);
$(let o = o.with_slice(&[$($see),*]);)?
o
}
};
(in $supersection:literal) => {
Opt(&[], None, $supersection, 1, None, None)
};
($section:literal) => {
Opt(&[], None, $section, 0, None, None)
};
() => {
Opt(&[], None, "", 0, None, None)
};
($($name:literal),* => $num:literal; $desc:literal $(, [$($see:literal),*])? $(, $def:literal)?) => {
{
let o = Opt(&[$($name),*], Some($num), $desc, 1, None, None);
$(let o = o.with_def($def);)?
$(let o = o.with_slice(&[$($see),*]);)?
o
}
};
($($name:literal),* => $num:literal; * $tabs:literal $desc:literal $(, [$($see:literal),*])? $(, $def:literal)?) => {
{
let o = Opt(&[$($name),*], Some($num), $desc, $tabs, None, None);
$(let o = o.with_def($def);)?
$(let o = o.with_slice(&[$($see),*]);)?
o
}
};
($($name:literal),*; $desc:literal $(, [$($see:literal),*])? $(, $def:literal)?) => {
{
let o = Opt(&[$($name),*], None, $desc, 1, None, None);
$(let o = o.with_def($def);)?
$(let o = o.with_slice(&[$($see),*]);)?
o
}
};
($($name:literal),*; * $tabs:literal $desc:literal $(, [$($see:literal),*])? $(, $def:literal)?) => {
{
let o = Opt(&[$($name),*], None, $desc, $tabs, None, None);
$(let o = o.with_def($def);)?
$(let o = o.with_slice(&[$($see),*]);)?
o
}
};
}
impl fmt::Display for Opt
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
{
use recolored::Colorize;
match (self.0, self.2, self.3) {
(&[], "", _) => return Ok(()),
(&[], desc, 2) => {
for line in desc.split('\n') {
writeln!(f, "> {}", line)?;
}
if let Some(strings) = self.5 {
writeln!(f, "> (see {})", strings.iter().map(|x| iter!["`".clear(), x.underline(), "`".clear()]).flat_join(", "))?;
}
return Ok(());
},
(&[], desc, 1) => return write!(f, "[{}]:", desc.bold().bright_blue()),
(&[], desc, _) if desc.starts_with("\n") => return write!(f, "\n>>> {}:", &desc[1..].bright_cyan()),
(&[], desc, _) => return write!(f, ">>> {}:", desc.bright_cyan()),
_ => (),
};
let wt = self.0.iter().join(", ");
if wt.starts_with("-") {
write!(f, " {}", wt)?;
} else {
write!(f, " {}", wt.bright_blue())?;
}
}
if let Some(mx) = self.1 {
use recolored::Colorize;
write!(f, " <{}>", mx.italic())?;
}
write!(f, ":")?;
for ch in std::iter::repeat('\t').take(self.3)
{
use std::fmt::Write;
f.write_char(ch)?;
}
write!(f,"{}", self.2)?;
{
use recolored::Colorize;
match self.5 {
Some(&[]) => (),
Some(strings) => {
write!(f, " (see {})", strings.iter().map(|x| iter!["`".clear(), x.underline(), "`".clear()]).flat_join(", "))?;
}
_ => (),
}
match self.4 {
Some("") => {
write!(f, " ({})", "default".bold().bright_red())?;
},
Some(string) => {
write!(f, " ({}: `{}`)", "default".bold().bright_red(), string.bright_blue())?;
},
None => (),
}
}
Ok(())
}
}
/// The `options` section
#[inline] fn options() -> impl Iterator<Item = Opt>
{
iter![
opt!(in "OPTIONS"),
opt!("--limit", "-l" => "max children"; "Limit the number of child processes running at once."),
opt!("--auto-limit", "-m"; * 2 "Limit to max number of CPUs + 1", ""),
opt!("--unlimit", "-U"; * 3 "No limit to number of processes spawned at once"),
opt!("--completion", "-c" => "action"; "Set the default action on completion", ["ACTIONS", "COMPLETION"], "ignore"),
opt!("--silent", "-s"; * 3 "Output no messages other than that of child processes"),
opt!("--stfu", "-S"; * 3 "Output no messages at all"),
opt!("\nChild I/O"),
opt!(where "Defaults for I/O redirections of children\nThese can be overwritten individually with explicit inputs."),
opt!("--stdout" => "where"; * 2 "Set default redirect option for childrens' `stdout`", ["ACTIONS"] ,"inherit"),
opt!("--stderr" => "where"; * 2 "Set default redirect option for childrens' `stderr`", ["ACTIONS"], "inherit"),
opt!("--stdin" => "where"; * 2 "Set default redirect option for childrens' `stdin`", ["ACTIONS"], "close"),
opt!()
]
}
/// The `actions` sections
#[inline] fn actions() -> impl Iterator<Item = Opt>
{
iter![
opt!(in "ACTIONS"),
opt!(where "Certain options have specific actions"),
opt!("I/O"),
opt!(where "Child I/O related operations"),
opt!("inherit"; * 3 "Inherit the stream from parent (this) process"),
opt!("close"; * 4 "Close the stream"),
opt!("ignore"; * 3 "Don't close the stream, but don't do anything with it"),
opt!("file" => "filename"; * 2 "Use this file as input / output.\n\t\t\t\tFor input, if the file is not readable or nonexistant, it causes a panic.\n\t\t\t\tFor output, it appends to the file or uses a new one for output.\n\t\t\t\tIf this is not possible, it causes a panic.\n\t\t\t\tTo overwrite the files instead of appending, use file:clobber"),
opt!("file:clobber" => "filename"; "Only valid for outputs, overwrite the file and write to it, instead of appending"),
opt!("stderr"; * 3 "Redirect stdout to the same location as stderr.\n\t\t\t\tIf there is a cycle detected here, or it is used for anything other that stdout, there is a panic"),
opt!("stdout"; * 3 "Redirect stderr to the same location as stdout.\n\t\t\t\tIf there is a cycle detected here, or it is used for anything other that stdout, there is a panic"),
opt!("Control"),
opt!(where "Controlling how processes are spawned\nThese can be overridden individually with explicit inputs"),
opt!("--uid" => "uid"; "Run as user specified by `uid`. Panics if `uid` is not a valid user id\n\t\t(note: Process must be ran as root to use this option)"),
opt!("--user" => "user"; "Same as `--uid`, except specified user is by name instead of uid"),
opt!("--gid" => "gid"; "Same as `--uid`, except runs as specific group instead of user"),
opt!("--group" => "group"; "Same as `--gid`, except specified group is by name instead of gid"),
opt!()
]
}
/// The `process completion` section.
#[inline] fn completion() -> impl Iterator<Item = Opt>
{
iter![
opt!(in "COMPLETION"),
opt!(where "Directives for how to handle abnormal process completion (i.e. non-zero exit codes)\nThis does not apply to `detached` processes", ["DETACHING"]),
opt!("ignore"; * 2 "Ignore process return code", ""),
opt!("supercede"; * 2 "If return code is a non-zero code, use it as the return code for the parent (this) process.\n\t\t\tIf there are multiple non-zero return codes, it is not specified which is returned"),
opt!("terminate"; * 2 "If return code is a non-zero code, abort all running children and return the code as this process' return code immediately"),
opt!("wait"; * 3 "Same as `terminate`, except waits for currently active processes to complete before aborting"),
opt!("log"; * 3 "Write the code to this process' `stderr` if non-zero"),
opt!("file" => "filename"; "If non-zero, write the code to a file if non-zero. If unable to write to the file, it is ignored"),
opt!("retry" => "number"; "If non-zero, restart the process up to `number` times.\n\t\t\tIf `number` if 'inf', retry indefinately (this can cause an infinite loop if program never completes with zero exit code)"),
opt!()
]
}
#[inline] fn environment() -> impl Iterator<Item = Opt>
{
iter![
opt!(in "ENVIRONMENT"),
opt!(where "Some environment variables can control the behaviour of the program also"),
opt!("RUST_LOG"),
opt!(where "Controls the logging level of the parent process\nThese can be surpressed entirely by `--silent` or `--stfu`\nIf the var is not set, the default level is used."),
opt!("trace"; * 2 "The most verbose logging"),
opt!("debug"; * 2 "Include debug messages"),
opt!("info"; * 2 "Include information messages"),
opt!("warn"; * 2 "Only warnings and errors"),
opt!("error"; * 2 "Only errors"),
opt!("none"; * 2 "No diagnostic messages", ""),
opt!()
]
}
/// Get the sections as display iterators.
/// Each item in the iterator is intended to be written as a single line.
pub fn sections() -> impl Iterator<Item = impl fmt::Display>
{
options()
.chain(actions())
.chain(completion())
.chain(environment())
}