Support optional trailing positional arguments

This commit is contained in:
topjohnwu
2025-10-19 03:53:53 -07:00
committed by John Wu
parent 57d9fc6099
commit 536e50c6e0
2 changed files with 39 additions and 53 deletions

View File

@@ -279,7 +279,7 @@ fn impl_from_args_struct(
.collect();
ensure_unique_names(errors, &fields);
ensure_only_last_positional_is_optional(errors, &fields);
ensure_only_trailing_positionals_are_optional(errors, &fields);
let impl_span = Span::call_site();
@@ -441,17 +441,22 @@ fn get_help_triggers(type_attrs: &TypeAttrs) -> Vec<String> {
})
}
/// Ensures that only the last positional arg is non-required.
fn ensure_only_last_positional_is_optional(errors: &Errors, fields: &[StructField<'_>]) {
/// Ensures that only trailing positional args are non-required.
fn ensure_only_trailing_positionals_are_optional(errors: &Errors, fields: &[StructField<'_>]) {
let mut first_non_required_span = None;
for field in fields {
if field.kind == FieldKind::Positional {
if let Some(first) = first_non_required_span {
if let Some(first) = first_non_required_span
&& field.optionality.is_required()
{
errors.err_span(
first,
"Only the last positional argument may be `Option`, `Vec`, or defaulted.",
"Only trailing positional arguments may be `Option`, `Vec`, or defaulted.",
);
errors.err(
&field.field,
"Later non-optional positional argument declared here.",
);
errors.err(&field.field, "Later positional argument declared here.");
return;
}
if !field.optionality.is_required() {

View File

@@ -58,8 +58,8 @@ struct Repack {
no_compress: bool,
#[argh(positional)]
img: Utf8CString,
#[argh(positional, default = r#"Utf8CString::from("new-boot.img")"#)]
out: Utf8CString,
#[argh(positional)]
out: Option<Utf8CString>,
}
#[derive(FromArgs)]
@@ -77,33 +77,24 @@ struct Sign {
#[argh(positional)]
img: Utf8CString,
#[argh(positional)]
args: Vec<Utf8CString>,
name: Option<Utf8CString>,
#[argh(positional)]
cert: Option<Utf8CString>,
#[argh(positional)]
key: Option<Utf8CString>,
}
#[derive(FromArgs)]
#[argh(subcommand, name = "extract")]
struct Extract {
#[argh(positional)]
payload: Utf8CString,
#[argh(positional)]
partition: Option<Utf8CString>,
#[argh(positional)]
outfile: Option<Utf8CString>,
}
impl FromArgs for Extract {
fn from_args(_command_name: &[&str], args: &[&str]) -> Result<Self, EarlyExit> {
let mut parse = PositionalArgParser(args.iter());
Ok(Extract {
payload: parse.required("payload.bin")?,
partition: parse.optional(),
outfile: parse.last_optional()?,
})
}
}
impl SubCommand for Extract {
const COMMAND: &'static CommandInfo = &CommandInfo {
name: "extract",
description: "",
};
}
#[derive(FromArgs)]
#[argh(subcommand, name = "hexpatch")]
struct HexPatch {
@@ -186,28 +177,15 @@ impl SubCommand for Compress {
};
}
#[derive(FromArgs)]
#[argh(subcommand, name = "decompress")]
struct Decompress {
#[argh(positional)]
file: Utf8CString,
#[argh(positional)]
out: Option<Utf8CString>,
}
impl FromArgs for Decompress {
fn from_args(_command_name: &[&str], args: &[&str]) -> Result<Self, EarlyExit> {
let mut iter = PositionalArgParser(args.iter());
Ok(Decompress {
file: iter.required("infile")?,
out: iter.last_optional()?,
})
}
}
impl SubCommand for Decompress {
const COMMAND: &'static CommandInfo = &CommandInfo {
name: "decompress",
description: "",
};
}
fn print_usage(cmd: &str) {
eprintln!(
r#"MagiskBoot - Boot Image Modification Tool
@@ -386,21 +364,24 @@ fn boot_main(cmds: CmdArgs) -> LoggedResult<i32> {
img,
out,
}) => {
repack(&img, &out, no_compress);
repack(
&img,
out.as_deref().unwrap_or(cstr!("new-boot.img")),
no_compress,
);
}
Action::Verify(Verify { img, cert }) => {
if !verify_cmd(&img, cert.as_deref()) {
return log_err!();
}
}
Action::Sign(Sign { img, args }) => {
let mut iter = args.iter();
sign_cmd(
&img,
iter.next().map(AsRef::as_ref),
iter.next().map(AsRef::as_ref),
iter.next().map(AsRef::as_ref),
)?;
Action::Sign(Sign {
img,
name,
cert,
key,
}) => {
sign_cmd(&img, name.as_deref(), cert.as_deref(), key.as_deref())?;
}
Action::Extract(Extract {
payload,