Browse Source

Update docstrings and arg parsing

Also add the most basic `debug_assert` test.
pull/26/head
Ian Chamberlain 2 years ago
parent
commit
583fc67fc6
No known key found for this signature in database
GPG Key ID: AE5484D09405AA60
  1. 112
      src/command.rs
  2. 83
      src/lib.rs
  3. 21
      src/main.rs

112
src/command.rs

@ -1,10 +1,7 @@ @@ -1,10 +1,7 @@
// TODO: docstrings for everything!!!
use clap::{Args, Parser, Subcommand};
#[derive(Parser, Debug)]
#[command(name = "cargo")]
#[command(bin_name = "cargo")]
#[command(name = "cargo", bin_name = "cargo")]
pub enum Cargo {
#[command(name = "3ds")]
Input(Input),
@ -13,20 +10,44 @@ pub enum Cargo { @@ -13,20 +10,44 @@ pub enum Cargo {
#[derive(Args, Debug)]
#[command(version, about)]
pub struct Input {
/// The cargo command to run. This command will be forwarded to the real
/// `cargo` with the appropriate arguments for a 3DS executable.
#[command(subcommand)]
pub cmd: CargoCommand,
pub cmd: CargoCmd,
}
/// The cargo command to run. This command will be forwarded to the real
/// `cargo` with the appropriate arguments for a 3DS executable.
///
/// If another command is passed which is not recognized, it will be passed
/// through unmodified to `cargo` with RUSTFLAGS set for the 3DS.
#[derive(Subcommand, Debug)]
#[command(allow_external_subcommands = true)]
pub enum CargoCmd {
/// Builds an executable suitable to run on a 3DS (3dsx).
Build(CargoArgs),
/// Builds an executable and sends it to a device with `3dslink`.
Run(Run),
/// Builds a test executable and sends it to a device with `3dslink`.
///
/// This can be used with `--test` for integration tests, or `--lib` for
/// unit tests (which require a custom test runner).
Test(Test),
/// Don't actually run any commands, just echo them to the console.
/// This is mostly intended for testing.
#[arg(long, hide = true)]
pub dry_run: bool,
// NOTE: it seems docstring + name for external subcommands are not rendered
// in help, but we might as well set them here in case a future version of clap
// does include them in help text.
/// Run any other `cargo` command with RUSTFLAGS set for the 3DS.
#[command(external_subcommand, name = "COMMAND")]
Passthrough(Vec<String>),
}
#[derive(Args, Debug)]
pub struct CargoArgs {
/// Pass additional options through to the `cargo` command.
///
/// To pass flags that start with `-`, you must use `--` to separate `cargo 3ds`
/// options from cargo options. All args after `--` will be passed through
/// options from cargo options. Any argument after `--` will be passed through
/// to cargo unmodified.
///
/// If one of the arguments is itself `--`, the args following that will be
@ -36,50 +57,31 @@ pub struct Input { @@ -36,50 +57,31 @@ pub struct Input {
///
/// > cargo 3ds run -- -- arg1 arg2
#[arg(trailing_var_arg = true)]
#[arg(allow_hyphen_values = true)]
#[arg(global = true)]
cargo_options: Vec<String>,
#[arg(name = "CARGO_ARGS")]
args: Vec<String>,
}
#[derive(Subcommand, Debug)]
pub enum CargoCommand {
/// Builds an executable suitable to run on a 3DS (3dsx).
Build,
/// Equivalent to `cargo check`.
Check,
/// Equivalent to `cargo clippy`.
Clippy,
/// Equivalent to `cargo doc`.
Doc,
/// Builds an executable and sends it to a device with `3dslink`.
Run(Run),
/// Builds a test executable and sends it to a device with `3dslink`.
///
/// This can be used with `--test` for integration tests, or `--lib` for
/// unit tests (which require a custom test runner).
Test(Test),
//
// TODO: this doesn't seem to work for some reason...
// #[command(external_subcommand)]
// Other(Vec<String>),
}
#[derive(Args, Debug)]
#[derive(Parser, Debug)]
pub struct Test {
/// If set, the built executable will not be sent to the device to run it.
#[arg(long)]
pub no_run: bool,
// The test command uses a superset of the same arguments as Run.
#[command(flatten)]
pub run_args: Run,
}
#[derive(Args, Debug)]
#[derive(Parser, Debug)]
pub struct Run {
/// Specify the IP address of the device to send the executable to.
///
/// Corresponds to 3dslink's `--address` arg, which defaults to automatically
/// finding the device.
#[arg(long, short = 'a')]
pub address: Option<String>,
pub address: Option<std::net::Ipv4Addr>,
/// Set the 0th argument of the executable when running it. Corresponds to
/// 3dslink's `--argv0` argument.
@ -88,7 +90,7 @@ pub struct Run { @@ -88,7 +90,7 @@ pub struct Run {
/// Start the 3dslink server after sending the executable. Corresponds to
/// 3dslink's `--server` argument.
#[arg(long, short = 's')]
#[arg(long, short = 's', default_value_t = false)]
pub server: bool,
/// Set the number of tries when connecting to the device to send the executable.
@ -96,9 +98,13 @@ pub struct Run { @@ -96,9 +98,13 @@ pub struct Run {
// Can't use `short = 'r'` because that would conflict with cargo's `--release/-r`
#[arg(long)]
pub retries: Option<usize>,
// Passthrough cargo options.
#[command(flatten)]
pub cargo_args: CargoArgs,
}
impl Input {
impl CargoArgs {
/// Get the args to be passed to the executable itself (not `cargo`).
pub fn cargo_opts(&self) -> &[String] {
self.split_args().0
@ -110,15 +116,31 @@ impl Input { @@ -110,15 +116,31 @@ impl Input {
}
fn split_args(&self) -> (&[String], &[String]) {
if let Some(split) = self.cargo_options.iter().position(|arg| arg == "--") {
let split = if &self.cargo_options[split] == "--" {
if let Some(split) = self
.args
.iter()
.position(|s| s == "--" || !s.starts_with('-'))
{
let split = if self.args[split] == "--" {
split + 1
} else {
split
};
self.cargo_options.split_at(split)
self.args.split_at(split)
} else {
(&self.cargo_options[..], &[])
(&self.args[..], &[])
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use clap::CommandFactory;
#[test]
fn verify_app() {
Cargo::command().debug_assert();
}
}

83
src/lib.rs

@ -1,8 +1,6 @@ @@ -1,8 +1,6 @@
extern crate core;
pub mod command;
use crate::command::{CargoCommand, Input};
use crate::command::{CargoCmd, Input};
use cargo_metadata::{Message, MetadataCommand};
use core::fmt;
use rustc_version::Channel;
@ -22,11 +20,11 @@ pub fn get_should_link(input: &mut Input) -> bool { @@ -22,11 +20,11 @@ pub fn get_should_link(input: &mut Input) -> bool {
// When running compile only commands, don't link the executable to the 3ds.
// Otherwise, link and run on the 3ds but do not run locally.
match input.cmd {
CargoCommand::Run(_) => true,
CargoCommand::Test(_) if !input.cargo_opts().contains(&"--no-run".to_string()) => {
// input.cargo_opts().push("--no-run".to_string());
true
}
CargoCmd::Run(_) => true,
// CargoCmd::Test(_) if !input.cargo_opts().contains(&"--no-run".to_string()) => {
// // input.cargo_opts().push("--no-run".to_string());
// true
// }
_ => false,
}
}
@ -35,41 +33,42 @@ pub fn get_should_link(input: &mut Input) -> bool { @@ -35,41 +33,42 @@ pub fn get_should_link(input: &mut Input) -> bool {
/// default to `json-render-diagnostics`.
pub fn get_message_format(input: &mut Input) -> String {
// Checks for a position within the args where '--message-format' is located
if let Some(pos) = input
.cargo_opts()
.iter()
.position(|s| s.starts_with("--message-format"))
{
// Remove the arg from list
let arg = input.cargo_opts()[pos].clone(); // TODO
// 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 {
input.cargo_opts()[pos].clone() // TODO
};
// Non-json formats are not supported so the executable exits.
if format.starts_with("json") {
format
} else {
eprintln!("error: non-JSON `message-format` is not supported");
process::exit(1);
}
} else {
// Default to 'json-render-diagnostics'
DEFAULT_MESSAGE_FORMAT.to_string()
}
todo!();
// if let Some(pos) = input
// .cargo_opts()
// .iter()
// .position(|s| s.starts_with("--message-format"))
// {
// // Remove the arg from list
// let arg = input.cargo_opts()[pos].clone(); // TODO
// // 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 {
// input.cargo_opts()[pos].clone() // TODO
// };
// // Non-json formats are not supported so the executable exits.
// if format.starts_with("json") {
// format
// } else {
// eprintln!("error: non-JSON `message-format` is not supported");
// process::exit(1);
// }
// } else {
// // Default to 'json-render-diagnostics'
// DEFAULT_MESSAGE_FORMAT.to_string()
// }
}
/// Build the elf that will be used to create other 3ds files.
/// The command built from [`make_cargo_build_command`] is executed
/// and the messages from the spawned process are parsed and returned.
pub fn build_elf(
cmd: CargoCommand,
cmd: CargoCmd,
message_format: &str,
args: &Vec<String>,
) -> (ExitStatus, Vec<Message>) {
@ -100,7 +99,7 @@ pub fn build_elf( @@ -100,7 +99,7 @@ pub fn build_elf(
/// Create the cargo build command, but don't execute it.
/// If there is no pre-built std detected in the sysroot, `build-std` is used.
pub fn make_cargo_build_command(
cmd: CargoCommand,
cmd: CargoCmd,
message_format: &str,
args: &Vec<String>,
) -> Command {
@ -114,11 +113,9 @@ pub fn make_cargo_build_command( @@ -114,11 +113,9 @@ pub fn make_cargo_build_command(
let mut command = Command::new(cargo);
let cmd = match cmd {
CargoCommand::Build | CargoCommand::Run(_) => "build",
CargoCommand::Test(_) => "test",
CargoCommand::Check => "check",
CargoCommand::Clippy => "clippy",
CargoCommand::Doc => "doc",
CargoCmd::Build(_) | CargoCmd::Run(_) => "build",
CargoCmd::Test(_) => "test",
CargoCmd::Passthrough(_) => todo!(),
};
command

21
src/main.rs

@ -1,9 +1,9 @@ @@ -1,9 +1,9 @@
use cargo_3ds::command::Cargo;
use cargo_3ds::command::{Cargo, CargoCmd, Input, Run, Test};
use cargo_3ds::{
build_3dsx, build_elf, build_smdh, check_rust_version, get_message_format, get_metadata,
get_should_link, link,
};
use clap::Parser;
use clap::{CommandFactory, FromArgMatches, Parser};
use std::process;
fn main() {
@ -12,10 +12,21 @@ fn main() { @@ -12,10 +12,21 @@ fn main() {
let Cargo::Input(mut input) = Cargo::parse();
dbg!(&input);
dbg!(input.cargo_opts());
dbg!(input.exe_args());
// let should_link = get_should_link(&mut input);
let cargo_args = match &input.cmd {
CargoCmd::Build(cargo_args)
| CargoCmd::Run(Run { cargo_args, .. })
| CargoCmd::Test(Test {
run_args: Run { cargo_args, .. },
..
}) => cargo_args,
CargoCmd::Passthrough(other) => todo!(),
};
dbg!(cargo_args.cargo_opts());
dbg!(cargo_args.exe_args());
// let
// let message_format = get_message_format(&mut input);
// let (status, messages) = build_elf(input.cmd, &message_format, &input.cargo_opts);

Loading…
Cancel
Save