use clap::builder::ValueParser;
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
use crate::command::PathKind;
use crate::command::Question;
use crate::path::Component;
pub fn build() -> Command {
Command::new("pathmut")
.version(crate_version!())
.about("Mutate path strings")
.args([parse_as_unix_arg(), parse_as_win_arg(), parse_as_url_arg()])
.subcommands([
get_command(),
remove_command(),
replace_command(),
set_command(),
has_command(),
is_command(),
convert_command(),
info_command(),
depth_command(),
])
.dont_delimit_trailing_values(true)
.arg_required_else_help(true)
.subcommand_value_name("COMMAND or COMPONENT")
.allow_external_subcommands(true)
.after_help(components_help_section())
}
fn parse_as_win_arg() -> Arg {
Arg::new("as-windows")
.global(true)
.short('w')
.long("as-windows")
.action(ArgAction::SetTrue)
.conflicts_with_all(["as-unix", "as-url"])
.help("Parse paths as windows paths")
}
fn parse_as_unix_arg() -> Arg {
Arg::new("as-unix")
.global(true)
.short('x')
.long("as-unix")
.action(ArgAction::SetTrue)
.conflicts_with_all(["as-windows", "as-url"])
.help("Parse paths as unix paths")
}
fn parse_as_url_arg() -> Arg {
Arg::new("as-url")
.global(true)
.short('u')
.long("as-url")
.action(ArgAction::SetTrue)
.conflicts_with_all(["as-windows", "as-unix"])
.help("Parse paths as URLs")
}
fn components_help_section() -> &'static str {
"\x1B[4;1mFile Components:\x1B[0m\n\
\x20 \x1B[1mext\x1B[0m File extension\n\
\x20 \x1B[1mstem\x1B[0m File stem\n\
\x20 \x1B[1mprefix\x1B[0m File prefix (before first dot)\n\
\x20 \x1B[1mname\x1B[0m File name\n\
\x20 \x1B[1mdisk\x1B[0m Disk of a windows path\n\
\x20 \x1B[1mwinprefix\x1B[0m Windows path prefix\n\n\
\x1B[4;1mURL Components:\x1B[0m\n\
\x20 \x1B[1mscheme\x1B[0m URL scheme (http, https, etc.)\n\
\x20 \x1B[1mhost\x1B[0m Hostname\n\
\x20 \x1B[1mport\x1B[0m Port number\n\
\x20 \x1B[1mpath\x1B[0m URL path\n\
\x20 \x1B[1mquery\x1B[0m Query string\n\
\x20 \x1B[1mfrag\x1B[0m Fragment identifier\n\
\x20 \x1B[1muser\x1B[0m Username\n\
\x20 \x1B[1mpass\x1B[0m Password\n\
\x20 \x1B[1morigin\x1B[0m scheme://host:port\n\
\x20 \x1B[1mtld\x1B[0m Top-level domain\n"
}
fn questions_help_section() -> &'static str {
"\x1B[4;1mQuestions:\x1B[0m\n\
\x20 \x1B[1mabsolute\x1B[0m\n\
\x20 \x1B[1mrelative\x1B[0m\n\
\x20 \x1B[1munix\x1B[0m\n\
\x20 \x1B[1mwindows\x1B[0m\n\
\x20 \x1B[1murl\x1B[0m\n"
}
fn component_arg() -> Arg {
Arg::new("component")
.required(true)
.value_parser(|s: &str| Component::try_from(s))
.help("Path component")
}
fn paths_arg() -> Arg {
Arg::new("path")
.required(true)
.action(ArgAction::Append)
.help("Path strings")
.value_parser(ValueParser::os_string())
}
fn question_arg() -> Arg {
Arg::new("question")
.required(true)
.help("Question to ask")
.value_parser(value_parser!(Question))
}
pub fn get_command() -> Command {
Command::new("get")
.about("Read a path component [default]")
.arg_required_else_help(true)
.args([component_arg(), paths_arg()])
.after_help(components_help_section())
}
pub fn has_command() -> Command {
Command::new("has")
.about("Check if a path component exists")
.arg_required_else_help(true)
.args(true_false_args())
.args([component_arg(), paths_arg()])
.after_help(components_help_section())
}
fn remove_command() -> Command {
Command::new("delete")
.about("Remove a path component")
.arg_required_else_help(true)
.args([component_arg(), paths_arg()])
.after_help(components_help_section())
}
fn replace_command() -> Command {
Command::new("replace")
.about("Replace an existing path component")
.arg_required_else_help(true)
.args([Arg::new("str")
.required(true)
.value_parser(ValueParser::os_string())])
.args([component_arg(), paths_arg()])
.after_help(components_help_section())
}
fn set_command() -> Command {
Command::new("set")
.about("Set a path component")
.arg_required_else_help(true)
.args([Arg::new("str")
.required(true)
.value_parser(ValueParser::os_string())])
.args([component_arg(), paths_arg()])
.after_help(components_help_section())
}
fn true_false_args() -> [Arg; 3] {
let any = Arg::new("any")
.help("[default] True if one path succeeds")
.long("any")
.action(ArgAction::SetTrue);
let all = Arg::new("all")
.help("True only if all paths succeed")
.long("all")
.action(ArgAction::SetTrue)
.conflicts_with("any");
let print = Arg::new("print")
.help("Print 'true' or 'false' to stdout instead of exit code")
.short('p')
.long("print")
.action(ArgAction::SetTrue);
[any, all, print]
}
fn is_command() -> Command {
Command::new("is")
.about("Ask questions about a file path")
.arg_required_else_help(true)
.args(true_false_args())
.args([question_arg(), paths_arg()])
.after_help(questions_help_section())
}
fn path_type_arg() -> Arg {
Arg::new("type")
.help("Type of path")
.required(true)
.value_parser(value_parser!(PathKind))
}
fn convert_command() -> Command {
Command::new("convert")
.about("Convert between unix and windows paths")
.arg_required_else_help(true)
.args([path_type_arg(), paths_arg()])
}
fn info_command() -> Command {
Command::new("info")
.about("Print information about paths")
.arg_required_else_help(true)
.args([
paths_arg(),
Arg::new("json")
.long("json")
.help("Output as JSON instead of YAML")
.action(ArgAction::SetTrue),
])
}
fn depth_command() -> Command {
Command::new("depth")
.about("Number of components before the last component.")
.arg_required_else_help(true)
.args([paths_arg()])
}