|
|
|
@ -23,7 +23,7 @@ pub struct Input {
@@ -23,7 +23,7 @@ pub struct Input {
|
|
|
|
|
#[command(allow_external_subcommands = true)] |
|
|
|
|
pub enum CargoCmd { |
|
|
|
|
/// Builds an executable suitable to run on a 3DS (3dsx).
|
|
|
|
|
Build(CargoArgs), |
|
|
|
|
Build(RemainingArgs), |
|
|
|
|
|
|
|
|
|
/// Builds an executable and sends it to a device with `3dslink`.
|
|
|
|
|
Run(Run), |
|
|
|
@ -43,7 +43,7 @@ pub enum CargoCmd {
@@ -43,7 +43,7 @@ pub enum CargoCmd {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Args, Debug)] |
|
|
|
|
pub struct CargoArgs { |
|
|
|
|
pub struct RemainingArgs { |
|
|
|
|
/// Pass additional options through to the `cargo` command.
|
|
|
|
|
///
|
|
|
|
|
/// To pass flags that start with `-`, you must use `--` to separate `cargo 3ds`
|
|
|
|
@ -101,12 +101,74 @@ pub struct Run {
@@ -101,12 +101,74 @@ pub struct Run {
|
|
|
|
|
|
|
|
|
|
// Passthrough cargo options.
|
|
|
|
|
#[command(flatten)] |
|
|
|
|
pub cargo_args: CargoArgs, |
|
|
|
|
pub cargo_args: RemainingArgs, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl CargoArgs { |
|
|
|
|
impl CargoCmd { |
|
|
|
|
/// Whether or not this command should build a 3DSX executable file.
|
|
|
|
|
pub fn should_build_3dsx(&self) -> bool { |
|
|
|
|
matches!(self, Self::Build(_) | Self::Run(_) | Self::Test(_)) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Whether or not the resulting executable should be sent to the 3DS with
|
|
|
|
|
/// `3dslink`.
|
|
|
|
|
pub fn should_link_to_device(&self) -> bool { |
|
|
|
|
match self { |
|
|
|
|
CargoCmd::Test(test) => !test.no_run, |
|
|
|
|
CargoCmd::Run(_) => true, |
|
|
|
|
_ => false, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub const DEFAULT_MESSAGE_FORMAT: &str = "json-render-diagnostics"; |
|
|
|
|
|
|
|
|
|
pub fn extract_message_format(&mut self) -> Result<Option<String>, String> { |
|
|
|
|
Self::extract_message_format_from_args(match self { |
|
|
|
|
CargoCmd::Build(args) => &mut args.args, |
|
|
|
|
CargoCmd::Run(run) => &mut run.cargo_args.args, |
|
|
|
|
CargoCmd::Test(test) => &mut test.run_args.cargo_args.args, |
|
|
|
|
CargoCmd::Passthrough(args) => args, |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn extract_message_format_from_args( |
|
|
|
|
cargo_args: &mut Vec<String>, |
|
|
|
|
) -> Result<Option<String>, String> { |
|
|
|
|
// Checks for a position within the args where '--message-format' is located
|
|
|
|
|
if let Some(pos) = cargo_args |
|
|
|
|
.iter() |
|
|
|
|
.position(|s| s.starts_with("--message-format")) |
|
|
|
|
{ |
|
|
|
|
// Remove the arg from list so we don't pass anything twice by accident
|
|
|
|
|
let arg = cargo_args.remove(pos); |
|
|
|
|
|
|
|
|
|
// Allows for usage of '--message-format=<format>' and also using space separation.
|
|
|
|
|
// Check for a '=' delimiter and use the second half of the split as the format,
|
|
|
|
|
// otherwise remove next arg which is now at the same position as the original flag.
|
|
|
|
|
let format = if let Some((_, format)) = arg.split_once('=') { |
|
|
|
|
format.to_string() |
|
|
|
|
} else { |
|
|
|
|
// Also need to remove the argument to the --message-format option
|
|
|
|
|
cargo_args.remove(pos) |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// Non-json formats are not supported so the executable exits.
|
|
|
|
|
if format.starts_with("json") { |
|
|
|
|
Ok(Some(format)) |
|
|
|
|
} else { |
|
|
|
|
Err(String::from( |
|
|
|
|
"error: non-JSON `message-format` is not supported", |
|
|
|
|
)) |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
Ok(None) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl RemainingArgs { |
|
|
|
|
/// Get the args to be passed to the executable itself (not `cargo`).
|
|
|
|
|
pub fn cargo_opts(&self) -> &[String] { |
|
|
|
|
pub fn cargo_args(&self) -> &[String] { |
|
|
|
|
self.split_args().0 |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -116,17 +178,8 @@ impl CargoArgs {
@@ -116,17 +178,8 @@ impl CargoArgs {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
fn split_args(&self) -> (&[String], &[String]) { |
|
|
|
|
if let Some(split) = self |
|
|
|
|
.args |
|
|
|
|
.iter() |
|
|
|
|
.position(|s| s == "--" || !s.starts_with('-')) |
|
|
|
|
{ |
|
|
|
|
let split = if self.args[split] == "--" { |
|
|
|
|
split + 1 |
|
|
|
|
} else { |
|
|
|
|
split |
|
|
|
|
}; |
|
|
|
|
self.args.split_at(split) |
|
|
|
|
if let Some(split) = self.args.iter().position(|s| s == "--") { |
|
|
|
|
self.args.split_at(split + 1) |
|
|
|
|
} else { |
|
|
|
|
(&self.args[..], &[]) |
|
|
|
|
} |
|
|
|
@ -143,4 +196,89 @@ mod tests {
@@ -143,4 +196,89 @@ mod tests {
|
|
|
|
|
fn verify_app() { |
|
|
|
|
Cargo::command().debug_assert(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn extract_format() { |
|
|
|
|
for (args, expected) in [ |
|
|
|
|
(&["--foo", "--message-format=json", "bar"][..], Some("json")), |
|
|
|
|
(&["--foo", "--message-format", "json", "bar"], Some("json")), |
|
|
|
|
( |
|
|
|
|
&[ |
|
|
|
|
"--foo", |
|
|
|
|
"--message-format", |
|
|
|
|
"json-render-diagnostics", |
|
|
|
|
"bar", |
|
|
|
|
], |
|
|
|
|
Some("json-render-diagnostics"), |
|
|
|
|
), |
|
|
|
|
( |
|
|
|
|
&["--foo", "--message-format=json-render-diagnostics", "bar"], |
|
|
|
|
Some("json-render-diagnostics"), |
|
|
|
|
), |
|
|
|
|
] { |
|
|
|
|
let mut cmd = CargoCmd::Build(RemainingArgs { |
|
|
|
|
args: args.iter().map(ToString::to_string).collect(), |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
assert_eq!( |
|
|
|
|
cmd.extract_message_format().unwrap(), |
|
|
|
|
expected.map(ToString::to_string) |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
if let CargoCmd::Build(args) = cmd { |
|
|
|
|
assert_eq!(args.args, vec!["--foo", "bar"]); |
|
|
|
|
} else { |
|
|
|
|
unreachable!(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn extract_format_err() { |
|
|
|
|
for args in [&["--message-format=foo"][..], &["--message-format", "foo"]] { |
|
|
|
|
let mut cmd = CargoCmd::Build(RemainingArgs { |
|
|
|
|
args: args.iter().map(ToString::to_string).collect(), |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
assert!(cmd.extract_message_format().is_err()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn split_run_args() { |
|
|
|
|
struct TestParam { |
|
|
|
|
input: &'static [&'static str], |
|
|
|
|
expected_cargo: &'static [&'static str], |
|
|
|
|
expected_exe: &'static [&'static str], |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for param in [ |
|
|
|
|
TestParam { |
|
|
|
|
input: &["--example", "hello-world", "--no-default-features"], |
|
|
|
|
expected_cargo: &["--example", "hello-world", "--no-default-features"], |
|
|
|
|
expected_exe: &[], |
|
|
|
|
}, |
|
|
|
|
TestParam { |
|
|
|
|
input: &["--example", "hello-world", "--", "--do-stuff", "foo"], |
|
|
|
|
expected_cargo: &["--example", "hello-world", "--"], |
|
|
|
|
expected_exe: &["--do-stuff", "foo"], |
|
|
|
|
}, |
|
|
|
|
TestParam { |
|
|
|
|
input: &["--lib", "--", "foo"], |
|
|
|
|
expected_cargo: &["--lib", "--"], |
|
|
|
|
expected_exe: &["foo"], |
|
|
|
|
}, |
|
|
|
|
TestParam { |
|
|
|
|
input: &["foo", "--", "bar"], |
|
|
|
|
expected_cargo: &["foo", "--"], |
|
|
|
|
expected_exe: &["bar"], |
|
|
|
|
}, |
|
|
|
|
] { |
|
|
|
|
let Run { cargo_args, .. } = |
|
|
|
|
Run::parse_from(std::iter::once(&"run").chain(param.input)); |
|
|
|
|
|
|
|
|
|
assert_eq!(cargo_args.cargo_args(), param.expected_cargo); |
|
|
|
|
assert_eq!(cargo_args.exe_args(), param.expected_exe); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|