use std::{env, path::PathBuf, process::ExitCode};
use clap::{
ArgAction, Args, ColorChoice, CommandFactory, Parser, Subcommand, ValueEnum,
builder::{Styles, styling::AnsiColor},
};
fn format_long_help() -> String {
format!(
"Sets a custom output format for the modlist.\n\n\
A string literal with {{PLACEHOLDER}} holes in it, one per field the cache holds.\n\
Backslash escapes (\\n, \\t, \\\\, \\{{, \\}}) are resolved by sculkr rather than by\n\
the shell, so quote the template and write \\n where you want a line break.\n\n\
Placeholders with no value for a given mod (CurseForge sends no license,\n\
Modrinth sends no authors) render as an empty string.\n\n\
Available placeholders:\n{}\n\n\
[default: {}]",
crate::format::placeholder_help(),
crate::format::DEFAULT_FORMAT
)
}
const HELP_STYLES: Styles = Styles::styled()
.header(AnsiColor::Yellow.on_default().bold().underline())
.usage(AnsiColor::Yellow.on_default().bold())
.literal(AnsiColor::Green.on_default().bold())
.placeholder(AnsiColor::Cyan.on_default());
#[derive(Debug, Parser)]
#[command(
name = "sculkr",
color = ColorChoice::Auto,
styles = HELP_STYLES,
version,
about = "Creates a modlist from packwiz",
long_about = "Utilizes the Modrinth and Curseforge API to output modlist information created via the packwiz CLI tool.",
propagate_version = true,
// DEBUG TESTING
// arg_required_else_help = true
)]
pub(crate) struct Cli {
#[arg(short, long, action = ArgAction::Count, global = true)]
pub(crate) verbose: u8,
#[arg(short, long, global = true)]
pub(crate) quiet: bool,
#[clap(
long,
short = 'f',
allow_hyphen_values = true,
default_value = crate::format::DEFAULT_FORMAT,
// clap debug-prints defaults, which would show the literal \n as \\n; the
// long help states it plainly instead.
hide_default_value = true,
long_help = format_long_help()
)]
pub(crate) format: String,
#[command(subcommand)]
pub(crate) command: Option<Command>,
}
#[derive(Debug, Subcommand)]
pub(crate) enum Command {
About,
Config {
#[arg(short, long, value_name = "PATH")]
path: Option<PathBuf>,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum Verbosity {
Quiet,
Normal,
Info,
Debug,
}
impl Verbosity {
pub fn resolve(verbose: u8, quiet: bool) -> Self {
match (quiet, verbose) {
(true, _) => Verbosity::Quiet,
(_, 0) => Verbosity::Normal,
(_, 1) => Verbosity::Info,
(..) => Verbosity::Debug,
}
}
pub fn to_level_filter(self) -> log::LevelFilter {
match self {
Verbosity::Quiet => log::LevelFilter::Error,
Verbosity::Normal => log::LevelFilter::Warn,
Verbosity::Info => log::LevelFilter::Info,
Verbosity::Debug => log::LevelFilter::Trace,
}
}
}
pub(crate) fn config(path: Option<PathBuf>) -> Result<(), String> {
match path {
Some(path) => println!("Config would go here, pulled from: {}", path.display()),
None => println!("Output resolving to None"),
}
Ok(())
}
pub(crate) fn about() -> Result<(), String> {
let cargo_toml = include_str!("../Cargo.toml");
if !cargo_toml.is_empty() {
let about_lines = cargo_toml.lines().skip(1).take(10);
for line in about_lines {
println!("{}", line)
}
}
std::process::exit(1);
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cli_definition_is_valid() {
Cli::command().debug_assert();
}
}