// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Render help text for a clap-style command.
fn render_help(cmd : Command) -> String {
render_help_with_usage_override(cmd, None)
}
///|
fn render_help_with_usage_override(
cmd : Command,
usage_line : String?,
) -> String {
let usage_line = usage_line.unwrap_or("Usage: \{cmd.name}\{usage_tail(cmd)}")
let about = cmd.about.unwrap_or("")
let about_section = if about == "" {
""
} else {
(
$|
$|
$|\{about}
)
}
let commands_section = render_section("Commands:", subcommand_entries(cmd))
let arguments_section = render_section("Arguments:", positional_entries(cmd))
let options_section = render_section(
"Options:",
option_entries(cmd),
keep_empty=true,
)
let groups_section = render_section("Groups:", group_entries(cmd))
(
$|\{usage_line}\{about_section}\{commands_section}\{arguments_section}\{options_section}\{groups_section}
$|
)
}
///|
fn usage_tail(cmd : Command) -> String {
let parts = [
required_option_usage(cmd),
if has_options(cmd) {
"[options]"
} else {
""
},
positional_usage(cmd),
subcommand_usage(cmd),
].filter(p => p != "")
if parts.is_empty() {
""
} else {
" " + parts.join(" ")
}
}
///|
fn subcommand_usage(cmd : Command) -> String {
if !has_subcommands_for_help(cmd) {
return ""
}
if cmd.subcommand_required {
""
} else {
"[command]"
}
}
///|
fn has_options(cmd : Command) -> Bool {
cmd.args.any(arg => {
!arg.hidden &&
arg.info is (OptionInfo(long~, short~, ..) | FlagInfo(long~, short~, ..)) &&
(long is Some(_) || short is Some(_)) &&
!is_required_arg(arg)
})
}
///|
fn required_option_usage(cmd : Command) -> String {
[
for arg in cmd.args if !(arg.hidden ||
!is_required_arg(arg) ||
arg.info is PositionalInfo(_)) &&
required_option_token(arg) is Some(token) => token
].join(" ")
}
///|
fn required_option_token(arg : Arg) -> String? {
let prefix = match arg.info {
OptionInfo(long~, short~, ..) | FlagInfo(long~, short~, ..) =>
if long is Some(long) {
Some("--\{long}")
} else if short is Some(short) {
Some("-\{short}")
} else {
None
}
PositionalInfo(_) => None
}
match prefix {
Some(prefix) =>
if arg.info is OptionInfo(_) {
Some("\{prefix} <\{arg.name}>")
} else {
Some(prefix)
}
None => None
}
}
///|
fn positional_usage(cmd : Command) -> String {
let parts = Array::new(capacity=cmd.args.length())
for arg in positional_args(cmd.args) {
if arg.hidden {
continue
}
let required = is_required_arg(arg)
if arg.multiple {
if required {
parts.push("<\{arg.name}...>")
} else {
parts.push("[\{arg.name}...]")
}
} else if required {
parts.push("<\{arg.name}>")
} else {
parts.push("[\{arg.name}]")
}
}
parts.join(" ")
}
///|
fn option_entries(cmd : Command) -> Array[String] {
let args = cmd.args
let display = Array::new(capacity=args.length() + 2)
let builtin_help_short = help_flag_enabled(cmd) &&
!has_short_option(args, 'h')
let builtin_help_long = help_flag_enabled(cmd) &&
!has_long_option(args, "help")
let builtin_version_short = version_flag_enabled(cmd) &&
!has_short_option(args, 'V')
let builtin_version_long = version_flag_enabled(cmd) &&
!has_long_option(args, "version")
let builtin_help_label = builtin_option_label(
builtin_help_short, builtin_help_long, "-h", "--help",
)
if builtin_help_label is Some(label) {
display.push((label, "Show help information."))
}
let builtin_version_label = builtin_option_label(
builtin_version_short, builtin_version_long, "-V", "--version",
)
if builtin_version_label is Some(label) {
display.push((label, "Show version information."))
}
for arg in args {
guard arg.info
is (OptionInfo(long~, short~, ..) | FlagInfo(long~, short~, ..)) &&
(long is Some(_) || short is Some(_)) else {
continue
}
if arg.hidden {
continue
}
let name = if arg.info is OptionInfo(_) {
"\{arg_display(arg)} <\{arg.name}>"
} else {
arg_display(arg)
}
display.push((name, arg_doc(arg)))
}
format_entries(display)
}
///|
fn has_long_option(args : Array[Arg], name : String) -> Bool {
args.any(arg => {
arg.info is (OptionInfo(long~, ..) | FlagInfo(long~, ..)) &&
long is Some(long) &&
long == name
})
}
///|
fn has_short_option(args : Array[Arg], value : Char) -> Bool {
args.any(arg => {
arg.info is (OptionInfo(short~, ..) | FlagInfo(short~, ..)) &&
short is Some(short) &&
short == value
})
}
///|
fn builtin_option_label(
has_short : Bool,
has_long : Bool,
short_label : String,
long_label : String,
) -> String? {
if has_short && has_long {
Some("\{short_label}, \{long_label}")
} else if has_short {
Some(short_label)
} else if has_long {
Some(long_label)
} else {
None
}
}
///|
fn positional_entries(cmd : Command) -> Array[String] {
format_entries(
[
for arg in positional_args(cmd.args) if !arg.hidden => {
(positional_display(arg), arg_doc(arg))
}
],
)
}
///|
fn subcommand_entries(cmd : Command) -> Array[String] {
let display = [
for sub in cmd.subcommands if !sub.hidden => {
let doc = sub.about.unwrap_or("")
let doc = if cmd.default_subcommand is Some(name) && name == sub.name {
if doc == "" {
"(default)"
} else {
"\{doc} (default)"
}
} else {
doc
}
(sub.name, doc)
}
]
if help_subcommand_enabled(cmd) {
display.push(("help", "Print help for the subcommand(s)."))
}
format_entries(display)
}
///|
fn group_entries(cmd : Command) -> Array[String] {
let display = Array::new(capacity=cmd.groups.length())
for group in cmd.groups {
let members = group_members(cmd, group)
if members == "" {
continue
}
display.push((group_label(group), members))
}
format_entries(display)
}
///|
fn render_section(
header : String,
lines : Array[String],
keep_empty? : Bool = false,
) -> String {
if lines.is_empty() {
if keep_empty {
(
$|
$|
$|\{header}
)
} else {
""
}
} else {
let body = lines.join("\n")
(
$|
$|
$|\{header}
$|\{body}
)
}
}
///|
fn format_entries(display : Array[(String, String)]) -> Array[String] {
let max_len = display.fold(init=0, (acc, item) => acc.max(item.0.length()))
fn fmt(name : String, doc : String) -> String {
let padding = " ".repeat(max_len - name.length() + 2)
" \{name}\{padding}\{doc}"
}
[
for item in display => fmt(item.0, item.1)
]
}
///|
fn arg_display(arg : Arg) -> String {
let parts = Array::new(capacity=2)
let (short, long) = match arg.info {
OptionInfo(short~, long~, ..)
| FlagInfo(short~, long~, ..)
| (PositionalInfo(_) with short = None, long = None) => (short, long)
}
if short is Some(short) {
parts.push("-\{short}")
}
if long is Some(long) {
if arg.info is FlagInfo(negatable=true, ..) {
parts.push("--[no-]\{long}")
} else {
parts.push("--\{long}")
}
}
if parts.is_empty() {
arg.name
} else {
parts.join(", ")
}
}
///|
fn positional_display(arg : Arg) -> String {
if arg.multiple {
"\{arg.name}..."
} else {
arg.name
}
}
///|
fn arg_doc(arg : Arg) -> String {
let notes = []
if arg.env is Some(env_name) {
notes.push("[env: \{env_name}]")
}
if arg.info
is (OptionInfo(default_values~, ..) | PositionalInfo(default_values~, ..)) &&
default_values is Some(values) {
if !values.is_empty() {
let defaults = values.join(", ")
notes.push("[default: \{defaults}]")
}
}
let help = arg.about.unwrap_or("")
if help == "" {
notes.join(" ")
} else if !notes.is_empty() {
let notes_text = notes.join(" ")
"\{help} \{notes_text}"
} else {
help
}
}
///|
fn has_subcommands_for_help(cmd : Command) -> Bool {
help_subcommand_enabled(cmd) || cmd.subcommands.any(sub => !sub.hidden)
}
///|
fn is_required_arg(arg : Arg) -> Bool {
if arg.required {
true
} else {
let (min, _) = arg_min_max(arg)
min > 0
}
}
///|
fn group_label(group : ArgGroup) -> String {
let tags = []
if group.required {
tags.push("[required]")
}
if !group.multiple {
tags.push("[exclusive]")
}
if tags.is_empty() {
group.name
} else {
let tags_text = tags.join(" ")
"\{group.name} \{tags_text}"
}
}
///|
fn group_members(cmd : Command, group : ArgGroup) -> String {
[
for arg in cmd.args if !arg.hidden && arg_in_group(arg, group) => {
group_member_display(arg)
}
].join(", ")
}
///|
fn group_member_display(arg : Arg) -> String {
let base = arg_display(arg)
match arg.info {
FlagInfo(_) => base
OptionInfo(_) => "\{base} <\{arg.name}>"
PositionalInfo(_) => base
}
}