use crate::assert_diag_snapshot;
use facet::Facet;
use figue as args;
#[test]
fn test_simplest_value_singleton_list_named() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::named, args::short = 'n')]
names: Vec<String>,
}
let args: Args = figue::from_slice(&["-n", "joe", "-n", "le", "-n", "rigolo"]).unwrap();
assert_eq!(args.names, vec!["joe", "le", "rigolo"]);
}
#[test]
fn test_simplest_value_singleton_list_positional() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::positional)]
names: Vec<String>,
}
let args: Args = figue::from_slice(&["joe", "le", "rigolo"]).unwrap();
assert_eq!(args.names, vec!["joe", "le", "rigolo"]);
}
#[test]
fn test_noargs_single_positional() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::positional)]
input: String,
}
let err = figue::from_slice::<Args>(&[]).unwrap_err();
assert_diag_snapshot!(err);
}
#[test]
fn test_noargs_vec_positional_default() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::positional, default)]
files: Vec<String>,
}
let args = figue::from_slice::<Args>(&[]).unwrap();
assert!(args.files.is_empty());
}
#[test]
fn test_noargs_vec_positional_no_default() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::positional)]
files: Vec<String>,
}
let err = figue::from_slice::<Args>(&[]).unwrap_err();
assert_diag_snapshot!(err);
}
#[test]
fn test_doubledash_nothing() {
#[derive(Facet, Debug, PartialEq)]
struct Args {}
let _args = figue::from_slice::<Args>(&["--"]).unwrap();
}
#[test]
fn test_doubledash_flags_before_dd() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::named, default)]
foo: bool,
#[facet(args::named, default)]
bar: bool,
#[facet(args::positional, default)]
args: Vec<String>,
}
let err = figue::from_slice::<Args>(&["--foo", "--bar", "--baz"]).unwrap_err();
assert_diag_snapshot!(err);
}
#[test]
fn test_doubledash_flags_across_dd() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::named, default)]
foo: bool,
#[facet(args::named, default)]
bar: bool,
#[facet(args::positional, default)]
args: Vec<String>,
}
let args = figue::from_slice::<Args>(&["--foo", "--bar", "--", "--baz"]).unwrap();
assert_eq!(
args,
Args {
foo: true,
bar: true,
args: vec!["--baz".to_string()],
}
);
}
#[test]
fn test_doubledash_flags_after_dd() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::named, default)]
foo: bool,
#[facet(args::named, default)]
bar: bool,
#[facet(args::positional, default)]
args: Vec<String>,
}
let args = figue::from_slice::<Args>(&["--", "--foo", "--bar", "--baz"]).unwrap();
assert_eq!(
args,
Args {
foo: false,
bar: false,
args: vec![
"--foo".to_string(),
"--bar".to_string(),
"--baz".to_string()
],
}
);
}
#[test]
fn test_doubledash_with_positional_after_named() {
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::short = 'r', args::named, default)]
release: bool,
#[facet(args::positional, default)]
ddc_args: Vec<String>,
}
let args = figue::from_slice::<Args>(&["--", "serve", "--port", "8888"]).unwrap();
assert_eq!(
args,
Args {
release: false,
ddc_args: vec![
"serve".to_string(),
"--port".to_string(),
"8888".to_string()
],
}
);
}
#[test]
fn test_doubledash_with_subcommand_and_trailing_args() {
#[derive(Facet, Debug, PartialEq)]
struct RunArgs {
#[facet(args::short = 'r', args::named, default)]
release: bool,
#[facet(args::positional, default)]
ddc_args: Vec<String>,
}
#[derive(Facet, Debug, PartialEq)]
#[repr(u8)]
enum Command {
Run(RunArgs),
}
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::subcommand)]
command: Command,
}
let args = figue::from_slice::<Args>(&["run", "--", "serve", "--port", "8888"]).unwrap();
assert_eq!(
args,
Args {
command: Command::Run(RunArgs {
release: false,
ddc_args: vec![
"serve".to_string(),
"--port".to_string(),
"8888".to_string()
],
}),
}
);
}
#[test]
fn test_doubledash_with_subcommand_name_collision() {
#[derive(Facet, Debug, PartialEq)]
struct BuildArgs {}
#[derive(Facet, Debug, PartialEq)]
struct RunArgs {
#[facet(args::short = 'r', args::named, default)]
release: bool,
#[facet(args::positional, default)]
ddc_args: Vec<String>,
}
#[derive(Facet, Debug, PartialEq)]
#[repr(u8)]
enum Command {
Build(BuildArgs),
Run(RunArgs),
}
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::subcommand)]
command: Command,
}
let args = figue::from_slice::<Args>(&["run", "--", "build"]).unwrap();
assert_eq!(
args,
Args {
command: Command::Run(RunArgs {
release: false,
ddc_args: vec!["build".to_string()],
}),
}
);
let args = figue::from_slice::<Args>(&["run", "--", "build", "deploy"]).unwrap();
assert_eq!(
args,
Args {
command: Command::Run(RunArgs {
release: false,
ddc_args: vec!["build".to_string(), "deploy".to_string()],
}),
}
);
}
#[test]
fn test_doubledash_with_subcommand_single_trailing_arg() {
#[derive(Facet, Debug, PartialEq)]
struct RunArgs {
#[facet(args::short = 'r', args::named, default)]
release: bool,
#[facet(args::positional, default)]
ddc_args: Vec<String>,
}
#[derive(Facet, Debug, PartialEq)]
#[repr(u8)]
enum Command {
Run(RunArgs),
}
#[derive(Facet, Debug, PartialEq)]
struct Args {
#[facet(args::subcommand)]
command: Command,
}
let args = figue::from_slice::<Args>(&["run", "--", "serve"]).unwrap();
assert_eq!(
args,
Args {
command: Command::Run(RunArgs {
release: false,
ddc_args: vec!["serve".to_string()],
}),
}
);
}
#[test]
fn test_subcommand_flatten_positional_vec_single_scalar() {
#[derive(Facet, Debug, PartialEq)]
struct GradleCommand {
#[facet(args::positional)]
tasks: Vec<String>,
#[facet(rename = "hide-logs", args::named, default = false)]
hide_logs: bool,
}
#[derive(Facet, Debug, PartialEq)]
#[repr(u8)]
enum Command {
Gradle {
#[facet(flatten)]
command: GradleCommand,
},
}
#[derive(Facet, Debug, PartialEq)]
struct Cli {
#[facet(args::subcommand)]
command: Command,
}
let cli = figue::from_slice::<Cli>(&["gradle", "runData"]).unwrap();
assert_eq!(
cli,
Cli {
command: Command::Gradle {
command: GradleCommand {
tasks: vec!["runData".to_string()],
hide_logs: false,
},
},
}
);
}
#[test]
fn test_subcommand_flatten_positional_vec_multiple_scalars() {
#[derive(Facet, Debug, PartialEq)]
struct GradleCommand {
#[facet(args::positional)]
tasks: Vec<String>,
#[facet(rename = "hide-logs", args::named, default = false)]
hide_logs: bool,
}
#[derive(Facet, Debug, PartialEq)]
#[repr(u8)]
enum Command {
Gradle {
#[facet(flatten)]
command: GradleCommand,
},
}
#[derive(Facet, Debug, PartialEq)]
struct Cli {
#[facet(args::subcommand)]
command: Command,
}
let cli =
figue::from_slice::<Cli>(&["gradle", "runData", "runGameTestServer", "test"]).unwrap();
assert_eq!(
cli,
Cli {
command: Command::Gradle {
command: GradleCommand {
tasks: vec![
"runData".to_string(),
"runGameTestServer".to_string(),
"test".to_string()
],
hide_logs: false,
},
},
}
);
}
#[test]
fn test_subcommand_flatten_positional_vec_error_path_is_consistent() {
#[derive(Facet, Debug, PartialEq)]
struct GradleCommand {
#[facet(args::positional)]
tasks: Vec<String>,
#[facet(rename = "hide-logs", args::named, default = false)]
hide_logs: bool,
}
#[derive(Facet, Debug, PartialEq)]
#[repr(u8)]
enum Command {
Gradle {
#[facet(flatten)]
command: GradleCommand,
},
}
#[derive(Facet, Debug, PartialEq)]
struct Cli {
#[facet(args::subcommand)]
command: Command,
}
let one = figue::from_slice::<Cli>(&["gradle", "runData"]).into_result();
let many = figue::from_slice::<Cli>(&["gradle", "runData", "test"]).into_result();
if let Err(err) = &one {
let msg = err.to_string();
assert!(
msg.contains("tasks"),
"error should reference tasks field: {msg}"
);
assert!(
msg.contains("command::Gradle.tasks") || msg.contains("command::Gradle.command.tasks"),
"error path should include Gradle.tasks (flattened or nested form): {msg}"
);
}
if let Err(err) = &many {
let msg = err.to_string();
assert!(
msg.contains("tasks"),
"error should reference tasks field: {msg}"
);
assert!(
msg.contains("command::Gradle.tasks") || msg.contains("command::Gradle.command.tasks"),
"error path should include Gradle.tasks (flattened or nested form): {msg}"
);
}
assert_eq!(
one.is_ok(),
many.is_ok(),
"inconsistent outcomes for one vs many positional task scalars: one={one:?}, many={many:?}"
);
}