diff --git a/package.json b/package.json index 7b9b6f8..1af5bb7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "creddy", - "version": "0.2.1", + "version": "0.2.2", "scripts": { "dev": "vite", "build": "vite build", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 809d492..de8ab69 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -68,36 +68,6 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" -[[package]] -name = "app" -version = "0.2.1" -dependencies = [ - "argon2", - "auto-launch", - "aws-config", - "aws-sdk-sts", - "aws-smithy-types", - "aws-types", - "chacha20poly1305", - "clap", - "dirs 5.0.1", - "is-terminal", - "netstat2", - "once_cell", - "serde", - "serde_json", - "sodiumoxide", - "sqlx", - "strum", - "strum_macros", - "sysinfo", - "tauri", - "tauri-build", - "tauri-plugin-single-instance", - "thiserror", - "tokio", -] - [[package]] name = "argon2" version = "0.5.0" @@ -975,6 +945,37 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "creddy" +version = "0.2.2" +dependencies = [ + "argon2", + "auto-launch", + "aws-config", + "aws-sdk-sts", + "aws-smithy-types", + "aws-types", + "chacha20poly1305", + "clap", + "dirs 5.0.1", + "gag", + "netstat2", + "once_cell", + "serde", + "serde_json", + "sodiumoxide", + "sqlx", + "strum", + "strum_macros", + "sysinfo", + "tauri", + "tauri-build", + "tauri-plugin-single-instance", + "thiserror", + "tokio", + "windows 0.48.0", +] + [[package]] name = "crossbeam-channel" version = "0.5.8" @@ -1347,6 +1348,17 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "filedescriptor" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7199d965852c3bac31f779ef99cbb4537f80e952e2d6aa0ffeb30cce00f4f46e" +dependencies = [ + "libc", + "thiserror", + "winapi", +] + [[package]] name = "filetime" version = "0.2.21" @@ -1529,6 +1541,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "gag" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a713bee13966e9fbffdf7193af71d54a6b35a0bb34997cd6c9519ebeb5005972" +dependencies = [ + "filedescriptor", + "tempfile", +] + [[package]] name = "gdk" version = "0.15.4" @@ -2099,18 +2121,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] - [[package]] name = "itertools" version = "0.10.5" @@ -4009,6 +4019,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe7e0f1d535e7cbbbab43c82be4fc992b84f9156c16c160955617e0260ebc449" dependencies = [ "anyhow", + "clap", "cocoa", "dirs-next", "embed_plist", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 267e53b..0d37c73 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "app" -version = "0.2.1" -description = "A Tauri App" -authors = ["you"] +name = "creddy" +version = "0.2.2" +description = "A friendly AWS credentials manager" +authors = ["Joseph Montanaro"] license = "" repository = "" -default-run = "app" +default-run = "creddy" edition = "2021" rust-version = "1.57" @@ -17,7 +17,7 @@ tauri-build = { version = "1.0.4", features = [] } [dependencies] serde_json = "1.0" serde = { version = "1.0", features = ["derive"] } -tauri = { version = "1.2", features = ["dialog", "os-all", "system-tray"] } +tauri = { version = "1.2", features = ["cli", "dialog", "os-all", "system-tray"] } tauri-plugin-single-instance = { git = "https://github.com/tauri-apps/plugins-workspace", branch = "dev" } sodiumoxide = "0.2.7" tokio = { version = ">=1.19", features = ["full"] } @@ -35,9 +35,11 @@ strum_macros = "0.24" auto-launch = "0.4.0" dirs = "5.0" clap = { version = "3.2.23", features = ["derive"] } -is-terminal = "0.4.7" +# is-terminal = "0.4.7" argon2 = { version = "0.5.0", features = ["std"] } chacha20poly1305 = { version = "0.10.1", features = ["std"] } +windows = { version = "0.48", features = ["Win32_Foundation", "Win32_System_Console"] } +gag = "1.0" [features] # by default Tauri runs in production mode diff --git a/src-tauri/src/cli.rs b/src-tauri/src/cli.rs index 17bce76..fa990a5 100644 --- a/src-tauri/src/cli.rs +++ b/src-tauri/src/cli.rs @@ -13,7 +13,6 @@ use tokio::{ io::{AsyncReadExt, AsyncWriteExt}, }; - use crate::app; use crate::config::AppConfig; use crate::credentials::{BaseCredentials, SessionCredentials}; @@ -23,6 +22,16 @@ use crate::errors::*; pub fn parser() -> Command<'static> { Command::new("creddy") .about("A friendly AWS credentials manager") + // we don't want the default help handling because it early-exits, + // and on Windows we need to setup the console before we can output + .disable_help_flag(true) + .arg( + Arg::new("help") + .short('h') + .long("help") + .action(ArgAction::SetTrue) + .help("Print this message or the help of the given subcommand(s)") + ) .subcommand( Command::new("run") .about("Launch Creddy") diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index cd82565..9df52f4 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -2,7 +2,6 @@ use std::net::Ipv4Addr; use std::path::PathBuf; use auto_launch::AutoLaunchBuilder; -use is_terminal::IsTerminal; use serde::{Serialize, Deserialize}; use sqlx::SqlitePool; @@ -96,7 +95,7 @@ pub fn get_or_create_db_path() -> Result { path.push("Creddy"); std::fs::create_dir_all(&path)?; - if cfg!(debug_assertions) && std::io::stdout().is_terminal() { + if cfg!(debug_assertions) { path.push("creddy.dev.db"); } else { diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 4065849..abd7e90 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,6 +3,20 @@ windows_subsystem = "windows" )] +#[cfg(windows)] +use { + std::fs::File, + std::os::windows::io::FromRawHandle, + std::os::raw::c_void, + gag::Redirect, + windows::Win32::System::Console::{ + AllocConsole, + AttachConsole, + GetStdHandle, + STD_OUTPUT_HANDLE, + STD_ERROR_HANDLE, + } +}; mod app; mod cli; @@ -15,18 +29,40 @@ mod state; mod server; mod tray; - use crate::errors::ErrorPopup; - +use std::io::Write; fn main() { - let res = match cli::parser().get_matches().subcommand() { - None | Some(("run", _)) => { + let args = cli::parser().get_matches(); + let help = matches!(args.get_one::("help"), Some(true)); + + // This is the only case that doesn't need a console + if let None | Some(("run", _)) = args.subcommand() { + if !help { app::run().error_popup("Creddy failed to start"); - Ok(()) - }, + return; + } + } + + // on Windows, need to do the whole allocate-a-console thing + #[cfg(windows)] + attach_console(); + // let (out, err) = setup_console(); + println!("Testing stdout"); + // writeln!(&mut out, "Testing allocated file"); + + if help { + // if we can't print help, we can't print an error, so just panic + dbg!(args.get_one::("help")); + cli::parser().print_help().unwrap(); + std::thread::sleep(std::time::Duration::from_secs(3)); + return; + } + + let res = match args.subcommand() { Some(("show", m)) => cli::show(m), Some(("exec", m)) => cli::exec(m), + // clap ensures that subcommand is either None or run/show/exec _ => unreachable!(), }; @@ -34,3 +70,43 @@ fn main() { eprintln!("Error: {e}"); } } + + +fn attach_console() { + unsafe { AttachConsole(u32::MAX); } +} + + + +fn setup_console() -> (Redirect, Redirect) { + let (mut stdout, stderr) = unsafe { + AllocConsole(); + // if we can't get handles to stdout/err, we can't display these errors, + // so there's no point in doing anything other than panicking + let stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE).unwrap(); + let stderr_handle = GetStdHandle(STD_ERROR_HANDLE).unwrap(); + + let mut f = File::create("C:\\Users\\Joe\\Downloads\\debug.txt").unwrap(); + writeln!(&mut f, "{stdout_handle:?}\n{:?}", stdout_handle.0 as *mut c_void).unwrap(); + + ( + File::from_raw_handle(stdout_handle.0 as *mut c_void), + File::from_raw_handle(stderr_handle.0 as *mut c_void), + ) + }; + + writeln!(&mut stdout, "Testing stdout before redirect") + .map_err(|e| log_err(e)) + .unwrap(); + + ( + Redirect::stdout(stdout).unwrap(), + Redirect::stderr(stderr).unwrap() + ) +} + + +fn log_err(e: impl std::error::Error) { + let mut f = File::create("C:\\Users\\Joe\\Downloads\\log.txt").unwrap(); + writeln!(&mut f, "{e}").unwrap(); +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 42c28d6..934a3dc 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -7,8 +7,8 @@ "distDir": "../dist" }, "package": { - "productName": "creddy", - "version": "0.2.1" + "productName": "Creddy", + "version": "0.2.2" }, "tauri": { "allowlist": { @@ -47,6 +47,14 @@ "timestampUrl": "" } }, + "cli": { + "description": "A friendly AWS credentials manager", + "subcommands": { + "run": { + "description": "Launch Creddy" + } + } + }, "security": { "csp": { "default-src": ["'self'"],