use crate::assert_help_snapshot;
use facet::Facet;
use figue as args;
use figue::FigueBuiltins;
#[derive(Facet, Debug)]
struct SimpleArgs {
#[facet(args::named, args::short = 'v')]
verbose: bool,
#[facet(args::named, args::short = 'j', args::label = "count")]
jobs: Option<usize>,
#[facet(args::positional)]
input: String,
#[facet(default, args::positional)]
output: Option<String>,
#[facet(flatten)]
builtins: FigueBuiltins,
}
#[test]
fn test_help_simple_struct() {
let config = figue::HelpConfig {
program_name: Some("myapp".to_string()),
version: Some("1.0.0".to_string()),
..Default::default()
};
let help = figue::generate_help::<SimpleArgs>(&config);
assert_help_snapshot!(help);
}
#[derive(Facet, Debug)]
struct GitArgs {
#[facet(args::subcommand)]
command: GitCommand,
#[facet(flatten)]
builtins: FigueBuiltins,
}
#[derive(Facet, Debug)]
#[repr(u8)]
#[allow(dead_code)]
enum GitCommand {
Clone {
#[facet(args::positional)]
url: String,
#[facet(default, args::positional)]
directory: Option<String>,
},
Log {
#[facet(args::named, args::short = 'n')]
count: Option<usize>,
#[facet(args::named)]
oneline: bool,
},
Remote {
#[facet(args::subcommand)]
action: RemoteAction,
},
}
#[derive(Facet, Debug)]
#[repr(u8)]
#[allow(dead_code)]
enum RemoteAction {
Add {
#[facet(args::positional)]
name: String,
#[facet(args::positional)]
url: String,
},
#[facet(rename = "rm")]
Remove {
#[facet(args::positional)]
name: String,
},
#[facet(rename = "ls")]
List {
#[facet(args::named, args::short = 'v')]
verbose: bool,
},
}
#[test]
fn test_help_with_subcommands() {
let config = figue::HelpConfig {
program_name: Some("git".to_string()),
version: Some("2.40.0".to_string()),
..Default::default()
};
let help = figue::generate_help::<GitArgs>(&config);
assert_help_snapshot!(help);
}
#[test]
fn test_help_enum_only() {
let config = figue::HelpConfig {
program_name: Some("git".to_string()),
..Default::default()
};
let help = figue::generate_help::<GitCommand>(&config);
assert_help_snapshot!(help);
}
#[test]
fn test_help_flags() {
let result = figue::from_slice::<SimpleArgs>(&["--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help());
assert!(err.help_text().is_some());
let result = figue::from_slice::<SimpleArgs>(&["-h"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help());
}
#[test]
fn test_html_help_flag_writes_html_file() {
let result = figue::from_slice::<SimpleArgs>(&["--html-help"]).into_result();
let Err(figue::DriverError::HtmlHelp { path }) = result else {
panic!("expected HTML help request");
};
assert_eq!(
path.file_name().and_then(|name| name.to_str()),
Some("index.html")
);
let html = std::fs::read_to_string(&path).expect("HTML help file should be readable");
assert!(html.contains("<!doctype html>"));
assert!(html.contains("A sample CLI application for testing help generation."));
assert!(html.contains("--html-help"));
assert!(html.contains("<INPUT>"));
}
#[test]
fn test_tuple_variant_subcommand_help_flattening() {
#[derive(Facet, Debug)]
struct BuildArgs {
#[facet(args::named, args::short = 'r')]
release: bool,
#[facet(args::named)]
no_spawn: bool,
#[facet(args::named)]
no_tui: bool,
}
#[derive(Facet, Debug)]
#[repr(u8)]
#[allow(dead_code)]
enum Command {
Build(BuildArgs),
Test {
#[facet(args::named, args::short = 'v')]
verbose: bool,
},
}
#[derive(Facet, Debug)]
struct Args {
#[facet(args::subcommand)]
command: Command,
#[facet(flatten)]
builtins: FigueBuiltins,
}
let config = figue::HelpConfig {
program_name: Some("myapp".to_string()),
..Default::default()
};
let help = figue::generate_help::<Args>(&config);
assert_help_snapshot!("tuple_variant_main_help", help);
}
#[derive(Facet, Debug)]
struct PkgManager {
#[facet(args::subcommand)]
command: PkgCommand,
#[facet(flatten)]
builtins: FigueBuiltins,
}
#[derive(Facet, Debug)]
#[repr(u8)]
#[allow(dead_code)]
enum PkgCommand {
Install {
#[facet(args::positional)]
package: String,
#[facet(args::named, args::short = 'g')]
global: bool,
#[facet(args::named, args::short = 'f')]
force: bool,
},
#[facet(rename = "rm")]
Remove {
#[facet(args::positional)]
package: String,
#[facet(args::named, args::short = 'y')]
yes: bool,
},
#[facet(rename = "ls")]
List {
#[facet(args::named, args::short = 'a')]
all: bool,
#[facet(args::named)]
json: bool,
},
}
#[test]
fn test_help_subcommand_install() {
let result = figue::from_slice::<PkgManager>(&["install", "--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help(), "expected help error, got: {:?}", err);
let help = err.help_text().expect("should have help text");
assert_help_snapshot!("subcommand_install_help", help);
}
#[test]
fn test_help_subcommand_remove() {
let result = figue::from_slice::<PkgManager>(&["rm", "--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help(), "expected help error, got: {:?}", err);
let help = err.help_text().expect("should have help text");
assert_help_snapshot!("subcommand_remove_help", help);
}
#[test]
fn test_help_subcommand_list() {
let result = figue::from_slice::<PkgManager>(&["ls", "--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help(), "expected help error, got: {:?}", err);
let help = err.help_text().expect("should have help text");
assert_help_snapshot!("subcommand_list_help", help);
}
#[test]
fn test_help_root_shows_all_subcommands() {
let result = figue::from_slice::<PkgManager>(&["--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help(), "expected help error, got: {:?}", err);
let help = err.help_text().expect("should have help text");
assert_help_snapshot!("subcommand_root_help", help);
}
#[test]
fn test_html_help_after_subcommand_still_writes_root_document() {
let result = figue::from_slice::<PkgManager>(&["install", "--html-help"]).into_result();
let Err(figue::DriverError::HtmlHelp { path }) = result else {
panic!("expected HTML help request");
};
let html = std::fs::read_to_string(&path).expect("HTML help file should be readable");
assert!(html.contains("<!doctype html>"));
assert!(html.contains("CLI with subcommands for testing subcommand-specific help"));
assert!(html.contains("Install a package"));
assert!(html.contains("Remove a package"));
assert!(html.contains("List installed packages"));
assert!(html.contains("--html-help"));
assert!(html.contains("id=\"command-install\""));
assert!(html.contains("window.FIGUE_INITIAL_ANCHOR = \"command-install\""));
}
#[derive(Facet, Debug)]
struct NestedCli {
#[facet(args::subcommand)]
command: TopLevel,
#[facet(flatten)]
builtins: FigueBuiltins,
}
#[derive(Facet, Debug)]
#[repr(u8)]
#[allow(dead_code)]
enum TopLevel {
Repo {
#[facet(args::subcommand)]
action: RepoCmd,
},
Version,
}
#[derive(Facet, Debug)]
#[repr(u8)]
#[allow(dead_code)]
enum RepoCmd {
Clone {
#[facet(args::positional)]
url: String,
#[facet(args::named)]
depth: Option<u32>,
#[facet(args::named, args::short = 'b')]
branch: Option<String>,
},
Push {
#[facet(args::positional, default)]
remote: Option<String>,
#[facet(args::named, args::short = 'f')]
force: bool,
},
}
#[test]
fn test_help_nested_subcommand_clone() {
let result = figue::from_slice::<NestedCli>(&["repo", "clone", "--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help(), "expected help error, got: {:?}", err);
let help = err.help_text().expect("should have help text");
assert_help_snapshot!("nested_subcommand_clone_help", help);
}
#[test]
fn test_help_nested_subcommand_push() {
let result = figue::from_slice::<NestedCli>(&["repo", "push", "--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help(), "expected help error, got: {:?}", err);
let help = err.help_text().expect("should have help text");
assert_help_snapshot!("nested_subcommand_push_help", help);
}
#[test]
fn test_help_nested_intermediate_level() {
let result = figue::from_slice::<NestedCli>(&["repo", "--help"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.is_help(), "expected help error, got: {:?}", err);
let help = err.help_text().expect("should have help text");
assert_help_snapshot!("nested_intermediate_repo_help", help);
}
#[test]
fn test_help_short_flag_h_works_in_subcommand() {
let result = figue::from_slice::<PkgManager>(&["install", "-h"]);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
err.is_help(),
"expected help error for -h flag, got: {:?}",
err
);
}