use std::{env, path::PathBuf, process::ExitCode};
use clap::{
ArgAction, Args, ColorChoice, CommandFactory, Parser, Subcommand, ValueEnum,
builder::{Styles, styling::AnsiColor},
};
use colored::Colorize;
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 = "Companion CLI for packwiz - generate modlists and track Minecraft modpack changes",
long_about = "A companion CLI application for packwiz that parses its output data to deliver advanced utility commands and extended features for Minecraft modpack development.",
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(
short,
long,
global = true,
value_name = "PATH",
help = format!("The path to the packwiz root directory. [default: {:?}]", PathBuf::from(".").canonicalize().unwrap_or(PathBuf::from("."))),
)]
pub(crate) path: Option<PathBuf>,
#[clap(short, long, global = true, value_name = "PATH")]
pub(crate) output: Option<PathBuf>,
#[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> {
println!();
println!(" {} {} {}", "⣿".cyan(), "sculkr".bold().cyan(), "⣿".cyan());
println!(" {}", "Companion CLI for packwiz".dimmed());
println!();
println!(" {:<12} {}", "Version:".bold(), env!("CARGO_PKG_VERSION"));
println!(" {:<12} {}", "Authors:".bold(), env!("CARGO_PKG_AUTHORS"));
println!(
" {:<12} {}",
"Description:".bold(),
env!("CARGO_PKG_DESCRIPTION")
);
println!(
" {:<12} {}",
"Repository:".bold(),
env!("CARGO_PKG_REPOSITORY")
);
println!(" {:<12} {}", "License:".bold(), env!("CARGO_PKG_LICENSE"));
println!();
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cli_definition_is_valid() {
Cli::command().debug_assert();
}
}