1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
use std::path::PathBuf;
use log::LevelFilter;
use structopt::StructOpt;
use crate::DebugOption;
/// A struct storing the CLI args taken by Monument. `StructOpt` will generate the argument
/// parsing/help code for us.
#[derive(Debug, Clone, StructOpt)]
#[structopt(name = "Monument", about = "Fast and flexible composition generator")]
pub struct CliArgs {
/// The name of the specification file for Monument (`*.toml`)
#[structopt(parse(from_os_str))]
pub input_file: PathBuf,
#[structopt(flatten)]
pub options: Options,
/// Makes Monument print more output (`-vv` will produce all output).
#[structopt(short, long = "verbose", parse(from_occurrences))]
pub verbosity: usize,
/// Makes Monument print less output (`-qq` will only produce errors).
#[structopt(short, long = "quiet", parse(from_occurrences))]
pub quietness: usize,
}
// Parameters passed directly into `monument_cli::run`, used to generated the [`monument::Config`]
// for the search. This isn't a doc-comment because doc comments overrides
// `#[structopt(about = "...")]`.
#[derive(Default, Debug, Clone, StructOpt)]
pub struct Options {
/// The maximum number of threads that Monument will use.
// TODO: Uncomment this once multi-threading is possible
// #[structopt(short = "T", long)]
pub num_threads: Option<usize>,
/// The maximum number of chunks in the chunk graph. Exceeding this during generation will
/// cause an error. Defaults to 100K.
#[structopt(long)]
pub graph_size_limit: Option<usize>,
/// The maximum number of bytes of heap memory that Monument's search routine can allocate.
/// Defaults to 80% of what's available.
#[structopt(short = "M", long, parse(try_from_str = parse_big_int))]
pub mem_limit: Option<usize>,
/// Debug options. `toml`, `params`, `search` and `graph` print the corresponding data
/// structures. `no-search` will run as normal but stop just before starting the full search.
#[structopt(short = "D", long)]
pub debug_option: Option<DebugOption>,
/// If `true` Monument will only display the update line, outputting no compositions until
/// the search is complete.
#[structopt(long = "only-update-line")]
pub only_display_update_line: bool,
}
impl CliArgs {
/// Parse the `-q`/`-v` args into the [`LevelFilter`] to give to the `log` library
pub fn log_level(&self) -> LevelFilter {
match self.verbosity as isize - self.quietness as isize {
x if x < -2 => LevelFilter::Off, // -qqq (or more `q`s)
-2 => LevelFilter::Error, // -qq
-1 => LevelFilter::Warn, // -q
0 => LevelFilter::Info, // <none of -q or -v>
1 => LevelFilter::Debug, // -v
2 => LevelFilter::Trace, // -vv
_ => LevelFilter::Trace, // -vvv (or more `v`s)
}
}
}
/// Parse a big integer like '100' or '140M'
fn parse_big_int(s: &str) -> anyhow::Result<usize> {
let (last_char_idx, last_char) = s.char_indices().last().unwrap();
let mut number_string = &s[..last_char_idx];
let mut multiplier = 1usize;
match last_char {
'k' | 'K' => multiplier = 1_000,
'm' | 'M' => multiplier = 1_000_000,
'g' | 'G' => multiplier = 1_000_000_000,
't' | 'T' => multiplier = 1_000_000_000_000,
'0'..='9' => number_string = s, // Part of the number
_ => {
return Err(anyhow::Error::msg(
"Expected number with a multiplier from [KMGT]",
));
}
}
Ok(number_string.parse::<usize>()? * multiplier)
}