diff --git a/Cargo.lock b/Cargo.lock index d5dd291..5a6a38b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -165,6 +165,8 @@ dependencies = [ "colored", "either", "enum_dispatch", + "env_logger", + "log", "rustc_version", "serde", "serde_json", @@ -296,6 +298,19 @@ dependencies = [ "syn", ] +[[package]] +name = "env_logger" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85cdab6a89accf66733ad5a1693a4dcced6aeff64602b634530dd73c1f3ee9f0" +dependencies = [ + "humantime", + "is-terminal", + "log", + "regex", + "termcolor", +] + [[package]] name = "errno" version = "0.3.4" @@ -341,6 +356,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "iana-time-zone" version = "0.1.58" @@ -383,9 +404,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "js-sys" -version = "0.3.64" +version = "0.3.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" +checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" dependencies = [ "wasm-bindgen", ] @@ -663,6 +684,15 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "termcolor" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.49" @@ -707,9 +737,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" +checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -717,9 +747,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" +checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" dependencies = [ "bumpalo", "log", @@ -732,9 +762,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" +checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -742,9 +772,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" +checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" dependencies = [ "proc-macro2", "quote", @@ -755,9 +785,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.87" +version = "0.2.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" [[package]] name = "winapi" diff --git a/Cargo.toml b/Cargo.toml index 0eea27e..25da0cb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,8 @@ clap = { version = "4.4.2", features = ["derive", "env"] } colored = "2.0.4" either = "1.9.0" enum_dispatch = "0.3.12" +env_logger = "0.10.0" +log = "0.4.20" rustc_version = "0.4.0" serde = { version = "1.0.188", features = ["derive"] } serde_json = "1.0.105" diff --git a/src/check.rs b/src/check.rs index fbd9db7..52746be 100644 --- a/src/check.rs +++ b/src/check.rs @@ -5,15 +5,15 @@ use std::{ }; use anyhow::{anyhow, Context}; +use log::error; use rustc_version::Channel; pub fn check_rust_version() { let rust_version = rustc_version::version_meta().unwrap(); if rust_version.channel > Channel::Nightly { - eprintln!("cargo-vita requires a nightly rustc version.\n"); - eprintln!( - "Do one of the following:\n \ + error!( + "cargo-vita requires a nightly rustc version. Do one of the following:\n \ - Run `rustup override set nightly` to use nightly in the current directory\n \ - Run cargo with +nightly flag.\n \ - Create a rust-toolchain.toml in the root with the following content:\n \ diff --git a/src/commands/build.rs b/src/commands/build.rs index d85fbbb..dd0d23b 100644 --- a/src/commands/build.rs +++ b/src/commands/build.rs @@ -12,6 +12,7 @@ use cargo_metadata::{camino::Utf8PathBuf, Artifact, Message, Package}; use clap::{Args, Subcommand}; use colored::Colorize; use either::Either; +use log::info; use tee::TeeReader; use walkdir::WalkDir; @@ -75,12 +76,10 @@ struct Vpk { struct BuildContext<'a> { command: &'a Build, sdk: String, - - verbose: u8, } impl<'a> BuildContext<'a> { - pub fn new(command: &'a Build, verbose: u8) -> anyhow::Result { + pub fn new(command: &'a Build) -> anyhow::Result { let sdk = std::env::var("VITASDK"); let sdk = sdk.or_else(|_| { bail!( @@ -89,11 +88,7 @@ impl<'a> BuildContext<'a> { ) })?; - Ok(Self { - command, - sdk, - verbose, - }) + Ok(Self { command, sdk }) } fn sdk(&self, path: &str) -> PathBuf { @@ -135,8 +130,8 @@ impl ExecutableArtifact { } impl Executor for Build { - fn execute(&self, verbose: u8) -> anyhow::Result<()> { - let ctx = BuildContext::new(self, verbose)?; + fn execute(&self) -> anyhow::Result<()> { + let ctx = BuildContext::new(self)?; match &self.cmd { BuildCmd::Elf => { @@ -221,7 +216,8 @@ impl<'a> BuildContext<'a> { command.env("PATH", path); } - // FIXME: A horrible solution, the same -Z flag will be used for all of the crates in a workspace. + // FIXME: move build-std to env/config.toml, since it is shared by all of the crates built + // This still works correctly when building only a single workspace crate though let (meta, _, _) = parse_crate_metadata(None)?; command @@ -249,14 +245,12 @@ impl<'a> BuildContext<'a> { .stdout(Stdio::piped()) .stderr(Stdio::inherit()); - if self.verbose > 0 { - println!("{} {command:?}", "Running cargo:".blue()); - } + info!("{}: {command:?}", "Running cargo".blue()); let mut process = command.spawn().context("Unable to spawn build process")?; let command_stdout = process.stdout.take().context("Build failed")?; - let reader = if self.verbose > 1 { + let reader = if log::max_level() >= log::LevelFilter::Trace { Either::Left(BufReader::new(TeeReader::new(command_stdout, io::stdout()))) } else { Either::Right(BufReader::new(command_stdout)) @@ -293,9 +287,7 @@ impl<'a> BuildContext<'a> { .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); - if self.verbose > 0 { - println!("{} {command:?}", "Stripping elf:".blue()); - } + info!("{}: {command:?}", "Stripping elf".blue()); if !command.status()?.success() { bail!("arm-vita-eabi-strip failed"); @@ -316,9 +308,7 @@ impl<'a> BuildContext<'a> { .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); - if self.verbose > 0 { - println!("{} {command:?}", "Creating velf:".blue()); - } + info!("{}: {command:?}", "Creating velf".blue()); if !command.status()?.success() { bail!("vita-elf-create failed"); @@ -341,9 +331,7 @@ impl<'a> BuildContext<'a> { .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); - if self.verbose > 0 { - println!("{} {command:?}", "Creating eboot:".blue()); - } + info!("{}: {command:?}", "Creating eboot".blue()); if !command.status()?.success() { bail!("vita-make-fself failed"); @@ -382,9 +370,7 @@ impl<'a> BuildContext<'a> { .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); - if self.verbose > 0 { - println!("{} {command:?}", "Creating sfo:".blue()); - } + info!("{}: {command:?}", "Creating sfo".blue()); if !command.status()?.success() { bail!("vita-mksfoex failed"); @@ -434,9 +420,7 @@ impl<'a> BuildContext<'a> { .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); - if self.verbose > 0 { - println!("{} {command:?}", "Building vpk:".blue()); - } + info!("{}: {command:?}", "Building vpk".blue()); if !command.status()?.success() { bail!("vita-pack-vpk failed") @@ -493,12 +477,10 @@ impl<'a> BuildContext<'a> { return Ok(()); } - let mut ftp = ftp::connect(conn, self.verbose)?; + let mut ftp = ftp::connect(conn)?; for (src, dest) in files { - if self.verbose > 0 { - println!("{} {src} {} {dest}", "Uploading file".blue(), "to".blue()) - } + info!("{} {src} {} {dest}", "Uploading".blue(), "file to".blue()); let src = File::open(src).context("Unable to open source file")?; ftp.put_file(dest, &mut BufReader::new(src)) @@ -521,7 +503,7 @@ impl<'a> BuildContext<'a> { title_id: Some(title_id.clone()), connection: conn.clone(), } - .execute(self.verbose)?; + .execute()?; } } diff --git a/src/commands/coredump.rs b/src/commands/coredump.rs index 29372bf..1feb179 100644 --- a/src/commands/coredump.rs +++ b/src/commands/coredump.rs @@ -6,6 +6,7 @@ use std::{ use anyhow::{bail, Context}; use clap::{Args, Subcommand}; use colored::Colorize; +use log::{info, warn}; use suppaftp::FtpError; use tempfile::NamedTempFile; @@ -51,18 +52,17 @@ pub struct Clean { } impl Executor for Coredump { - fn execute(&self, verbose: u8) -> anyhow::Result<()> { + fn execute(&self) -> anyhow::Result<()> { match &self.cmd { CoredumpCmd::Parse(args) => { - let mut ftp = ftp::connect(&args.connection, verbose)?; + let mut ftp = ftp::connect(&args.connection)?; + ftp.cwd("ux0:/data/") .context("Unable to cwd to ux0:/data/")?; let files = ftp.list(None).context("Unable to list files in cwd")?; if let Some(coredump) = find_core_dumps(&files).max() { - if verbose > 0 { - println!("{} {coredump}", "Downloading file:".blue()) - } + info!("{}: {coredump}", "Downloading file".blue()); let mut reader = ftp .retr_as_buffer(coredump) .context("Unable to download coredump")?; @@ -113,19 +113,17 @@ impl Executor for Coredump { .stdout(Stdio::inherit()) .stderr(Stdio::inherit()); - if verbose > 0 { - println!("{} {command:?}", "Parsing coredump:".blue()); - } + info!("{}: {command:?}", "Parsing coredump".blue()); if !command.status()?.success() { bail!("vita-parse-core failed"); } - } else if verbose > 0 { - println!("{}", "No coredump files found.".yellow()) + } else { + warn!("{}", "No coredump files found.".yellow()) } } CoredumpCmd::Clean(args) => { - let mut ftp = ftp::connect(&args.connection, verbose)?; + let mut ftp = ftp::connect(&args.connection)?; ftp.cwd("ux0:/data/") .context("Unable to cwd to ux0:/data/")?; @@ -134,9 +132,7 @@ impl Executor for Coredump { for file in find_core_dumps(&files) { counter += 1; - if verbose > 0 { - println!("{} {file}", "Deleting file:".blue()) - } + info!("{}: {file}", "Deleting file".blue()); match ftp.rm(file) { Ok(_) => {} @@ -146,8 +142,8 @@ impl Executor for Coredump { } } - if counter == 0 && verbose > 0 { - println!("{}", "No coredump files found.".yellow()) + if counter == 0 { + warn!("{}", "No coredump files found.".yellow()) } } } diff --git a/src/commands/logs.rs b/src/commands/logs.rs index 92163fe..3dbf8b4 100644 --- a/src/commands/logs.rs +++ b/src/commands/logs.rs @@ -3,6 +3,7 @@ use std::{io::Read, net::TcpListener}; use anyhow::Context; use clap::Args; use colored::Colorize; +use log::{debug, error, info}; use super::Executor; @@ -13,10 +14,8 @@ pub struct Logs { } impl Executor for Logs { - fn execute(&self, verbose: u8) -> anyhow::Result<()> { - if verbose > 0 { - println!("{} {}", "Starting TCP server on port".blue(), self.port); - } + fn execute(&self) -> anyhow::Result<()> { + info!("{} {}", "Starting TCP server on port".blue(), self.port); let listener = TcpListener::bind(("0.0.0.0", self.port)).context("Unable to start TCP server")?; @@ -24,29 +23,25 @@ impl Executor for Logs { for stream in listener.incoming() { match stream { Ok(mut client) => { - if verbose > 1 { - println!( - "{}: {}", - "Accepted connection from".blue(), - client.peer_addr().context("Unable to get peer address")? - ); - } + debug!( + "{}: {}", + "Accepted connection from".blue(), + client.peer_addr().context("Unable to get peer address")? + ); std::thread::spawn(move || { let mut buffer = [0; 1024]; loop { match client.read(&mut buffer) { Ok(0) => { - if verbose > 1 { - println!("{}", "Client disconnected".blue()); - } + debug!("{}", "Client disconnected".blue()); break; } Ok(bytes_read) => { print!("{}", String::from_utf8_lossy(&buffer[..bytes_read])) } Err(e) => { - eprintln!("{}: {}", "Error reading from client".red(), e); + error!("{}: {}", "Error reading from client", e); break; } } @@ -54,7 +49,7 @@ impl Executor for Logs { }); } Err(e) => { - eprintln!("Error accepting connection: {}", e); + error!("Error accepting connection: {}", e); } } } diff --git a/src/commands/mod.rs b/src/commands/mod.rs index c64db61..e820695 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -66,7 +66,7 @@ pub enum CargoCmd { #[enum_dispatch(CargoCmd)] pub trait Executor { - fn execute(&self, verbose: u8) -> anyhow::Result<()>; + fn execute(&self) -> anyhow::Result<()>; } #[derive(Args, Debug)] diff --git a/src/commands/reboot.rs b/src/commands/reboot.rs index b5fd39d..8269aaf 100644 --- a/src/commands/reboot.rs +++ b/src/commands/reboot.rs @@ -11,10 +11,10 @@ pub struct Reboot { } impl Executor for Reboot { - fn execute(&self, verbose: u8) -> anyhow::Result<()> { + fn execute(&self) -> anyhow::Result<()> { let ip = &self.connection.vita_ip; let port = self.connection.cmd_port; - nc(verbose, ip, port, "reboot")?; + nc(ip, port, "reboot")?; Ok(()) } diff --git a/src/commands/run.rs b/src/commands/run.rs index 6a3c8a4..28df50d 100644 --- a/src/commands/run.rs +++ b/src/commands/run.rs @@ -15,7 +15,7 @@ pub struct Run { } impl Executor for Run { - fn execute(&self, verbose: u8) -> anyhow::Result<()> { + fn execute(&self) -> anyhow::Result<()> { let title_id = match &self.title_id { Some(title_id) => title_id.clone(), None => parse_crate_metadata(None)?.0.title_id @@ -25,8 +25,8 @@ impl Executor for Run { let ip = &self.connection.vita_ip; let port = self.connection.cmd_port; - nc(verbose, ip, port, "destroy")?; - nc(verbose, ip, port, &format!("launch {title_id}"))?; + nc(ip, port, "destroy")?; + nc(ip, port, &format!("launch {title_id}"))?; Ok(()) } diff --git a/src/commands/upload.rs b/src/commands/upload.rs index 6389bfa..8c40ed3 100644 --- a/src/commands/upload.rs +++ b/src/commands/upload.rs @@ -3,6 +3,7 @@ use std::{fs::File, io::BufReader, path::Path}; use anyhow::{bail, Context}; use clap::Args; use colored::Colorize; +use log::{debug, info}; use suppaftp::FtpError; use walkdir::WalkDir; @@ -25,13 +26,13 @@ pub struct Upload { } impl Executor for Upload { - fn execute(&self, verbose: u8) -> anyhow::Result<()> { + fn execute(&self) -> anyhow::Result<()> { let source = Path::new(&self.source); if !source.exists() { bail!("Source path does not exist"); } - let mut ftp = ftp::connect(&self.connection, verbose)?; + let mut ftp = ftp::connect(&self.connection)?; let destination = if self.destination.ends_with('/') { format!( @@ -47,12 +48,11 @@ impl Executor for Upload { }; if source.is_file() { - if verbose > 0 { - println!( - "{}", - format!("Uploading {source:?} to {destination}").blue() - ); - } + info!( + "{} {source:?} {} {destination}", + "Uploading".blue(), + "to".blue(), + ); ftp.put_file( &destination, @@ -73,12 +73,11 @@ impl Executor for Upload { ); if file.file_type().is_file() { - if verbose > 0 { - println!( - "{}", - format!("Uploading {source_path:?} to {destination}").blue() - ); - } + info!( + "{} {source_path:?} {} {destination}", + "Uploading".blue(), + "to".blue(), + ); ftp.put_file( &destination, @@ -93,23 +92,20 @@ impl Executor for Upload { } // For some reason doing multiple cwd in a single connection breaks vitacompanion, - // So we'll skip directory creation errors. + // Some of these errors are benign (e.g. when a directory already exists), + // so if an error happens it does not return Err, just print a debug log. if ftp.cwd(&destination).is_err() { - if verbose > 0 { - println!("{} {destination}", "Creating directory".blue()); - } + info!("{} {destination}", "Creating directory".blue()); match ftp.mkdir(&destination) { Ok(_) => {} Err(FtpError::UnexpectedResponse(e)) if String::from_utf8_lossy(&e.body) .starts_with("226 Directory created.") => {} Err(e) => { - if verbose > 1 { - eprintln!( - "{} {destination}, {e}", - "Unable to create directory: ".red() - ); - } + debug!( + "{}: {destination}, {e}", + "Unable to create directory ".red() + ); } }; } diff --git a/src/ftp.rs b/src/ftp.rs index 492b276..64d9b12 100644 --- a/src/ftp.rs +++ b/src/ftp.rs @@ -2,17 +2,16 @@ use std::ops::Deref; use anyhow::Context; use colored::Colorize; +use log::info; use suppaftp::FtpStream; use crate::commands::ConnectionArgs; -pub fn connect(conn: &ConnectionArgs, verbose: u8) -> anyhow::Result { +pub fn connect(conn: &ConnectionArgs) -> anyhow::Result { let ip = conn.vita_ip.deref(); let port = conn.ftp_port; - if verbose > 0 { - println!("{} {ip}:{port}", "Connecting to Vita FTP server:".blue()) - } + info!("{} {ip}:{port}", "Connecting to Vita FTP server".blue()); let ftp = FtpStream::connect((ip, port)).context("Unable to connect to Vita FTP server")?; diff --git a/src/main.rs b/src/main.rs index 80ebbea..deb691e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,16 +8,31 @@ use check::check_rust_version; use clap::Parser; use colored::Colorize; use commands::{Cargo, Executor}; +use log::error; fn main() { - check_rust_version(); let _ = check::set_cargo_config_env(); let Cargo::Input(input) = Cargo::parse(); - match input.cmd.execute(1 + input.verbose - input.quiet as u8) { + + env_logger::Builder::new() + .format_timestamp(None) + .format_target(false) + .filter_level(match (input.quiet, input.verbose) { + (true, _) => log::LevelFilter::Error, + (false, 0) => log::LevelFilter::Info, + (false, 1) => log::LevelFilter::Debug, + (false, _) => log::LevelFilter::Trace, + }) + .init(); + + check_rust_version(); + + let Cargo::Input(input) = Cargo::parse(); + match input.cmd.execute() { Ok(_) => {} Err(e) => { - eprintln!("{} {}", "Error:".bold().red(), format!("{e:?}").red()); + error!("{}", format!("{e:?}").red()); std::process::exit(1); } } diff --git a/src/nc.rs b/src/nc.rs index d193611..b79e3e2 100644 --- a/src/nc.rs +++ b/src/nc.rs @@ -5,11 +5,11 @@ use std::{ use anyhow::Context; use colored::Colorize; +use log::info; + +pub fn nc(ip: &str, port: u16, command: &str) -> anyhow::Result<()> { + info!("{} {ip}:{port} -> {command}", "Sending command to".blue()); -pub fn nc(verbose: u8, ip: &str, port: u16, command: &str) -> anyhow::Result<()> { - if verbose > 0 { - println!("{} {ip}:{port} -> {command}", "Sending command to".blue()); - } let mut stream = TcpStream::connect((ip, port)).context("Unable to connect to command server")?; let command = format!("{}\n", command); @@ -21,9 +21,8 @@ pub fn nc(verbose: u8, ip: &str, port: u16, command: &str) -> anyhow::Result<()> stream .read_to_string(&mut response) .context("Unable to read output")?; - if verbose > 0 { - println!("{} {}", "Server response:".yellow(), response); - } + + info!("{}: {}", "Server response".yellow(), response); Ok(()) }