///|
fn cli_command() -> @argparse.Command {
Command(
"justhtml",
flags=[
FlagArg("help", short='h', long="help"),
FlagArg("version", long="version"),
FlagArg("unsafe", long="unsafe"),
FlagArg("cleanup", long="cleanup"),
FlagArg("first", long="first"),
FlagArg("fragment", long="fragment"),
FlagArg("strict", long="strict"),
FlagArg("separator-blocks-only", long="separator-blocks-only"),
FlagArg("strip", long="strip"),
FlagArg("no-strip", long="no-strip"),
],
options=[
cli_option("format"),
cli_option("output"),
cli_option("selector"),
cli_option("allow-tags"),
cli_option("separator"),
],
positionals=[PositionArg("path", num_args=ValueRange(lower=0, upper=1))],
disable_help_flag=true,
disable_version_flag=true,
disable_help_subcommand=true,
)
}
///|
fn cli_option(name : String) -> @argparse.OptionArg {
OptionArg(name, action=Append, allow_hyphen_values=true)
}
///|
fn cli_matches_flag(matches : @argparse.Matches, name : String) -> Bool {
match matches.flags.get(name) {
Some(value) => value
None => false
}
}
///|
fn cli_first_value(matches : @argparse.Matches, name : String) -> String? {
match matches.values.get(name) {
Some(values) => if values.length() == 0 { None } else { Some(values[0]) }
None => None
}
}
///|
fn cli_last_value(matches : @argparse.Matches, name : String) -> String? {
match matches.values.get(name) {
Some(values) => Some(values[values.length() - 1])
None => None
}
}
///|
fn cli_first_line(message : String) -> String {
let lines = [ for line in message.split("\n") => line ]
lines[0].to_owned()
}
///|
fn cli_argparse_error(message : String) -> CliResult {
cli_exit(2, stderr="\{cli_first_line(message)}\n\n\{cli_help()}")
}
///|
fn cli_parse_format(value : String) -> CliFormat? {
match value {
"html" => Some(CliHtml)
"text" => Some(CliText)
"markdown" => Some(CliMarkdown)
_ => None
}
}
///|
fn cli_strip_conflict_message(args : ArrayView[String]) -> String {
for arg in args {
match arg {
"--strip" => return "--no-strip conflicts with --strip"
"--no-strip" => return "--strip conflicts with --no-strip"
_ => ()
}
}
"--strip conflicts with --no-strip"
}
///|
fn cli_parse_args(args : ArrayView[String]) -> CliParseResult {
if args.length() == 0 {
return CliParseExit(cli_exit(1, stderr=cli_help()))
}
let command = cli_command()
let matches = command.parse(argv=args, env={}) catch {
error => return CliParseExit(cli_argparse_error(error.to_string()))
}
if cli_matches_flag(matches, "help") {
return CliParseExit(cli_exit(0, stdout=cli_help()))
}
if cli_matches_flag(matches, "version") {
return CliParseExit(cli_exit(0, stdout="justhtml \{cli_version()}\n"))
}
let options = cli_default_options()
match cli_last_value(matches, "format") {
Some(value) =>
match cli_parse_format(value) {
Some(format) => options.format = format
None =>
return CliParseExit(
cli_arg_error("--format must be one of: html, text, markdown"),
)
}
None => ()
}
options.output_path = cli_last_value(matches, "output")
options.selector = cli_last_value(matches, "selector")
options.allow_tags = cli_last_value(matches, "allow-tags")
match cli_last_value(matches, "separator") {
Some(value) => options.separator = value
None => ()
}
options.unsafe_mode = cli_matches_flag(matches, "unsafe")
options.cleanup = cli_matches_flag(matches, "cleanup")
options.first = cli_matches_flag(matches, "first")
options.fragment = cli_matches_flag(matches, "fragment")
options.strict = cli_matches_flag(matches, "strict")
options.separator_blocks_only = cli_matches_flag(
matches, "separator-blocks-only",
)
let strip = cli_matches_flag(matches, "strip")
let no_strip = cli_matches_flag(matches, "no-strip")
if strip && no_strip {
return CliParseExit(cli_arg_error(cli_strip_conflict_message(args)))
}
if no_strip {
options.strip = false
} else if strip {
options.strip = true
}
match cli_first_value(matches, "path") {
Some(path) => {
options.path = Some(path)
CliParsed(options)
}
None => CliParseExit(cli_exit(1, stderr=cli_help()))
}
}
///|
/// Parse CLI arguments and report whether the caller must read an input path.
///
/// Use this when integrating with an environment that performs its own file or
/// standard-input IO. For direct byte input, use `run_cli_bytes`.
pub fn cli_read_plan(args : ArrayView[String]) -> CliReadPlan {
match cli_parse_args(args) {
CliParseExit(result) => CliImmediate(result)
CliParsed(options) => CliReadPath(options.path.unwrap())
}
}