use std::process::Command; use std::time::Duration; use tauri::Manager; use tokio::time::sleep; use crate::app::APP; use crate::errors::*; use crate::state::AppState; pub async fn launch(use_base: bool) -> Result<(), LaunchTerminalError> { let app = APP.get().unwrap(); let state = app.state::(); // register_terminal_request() returns Err if there is another request pending if state.register_terminal_request().await.is_err() { return Ok(()); } let mut cmd = { let config = state.config.read().await; let mut cmd = Command::new(&config.terminal.exec); cmd.args(&config.terminal.args); cmd }; // if session is locked, wait for credentials from frontend if state.is_locked().await { let lease = state.acquire_visibility_lease(0).await .map_err(|_e| LaunchTerminalError::NoMainWindow)?; // automate conversion eventually? let (tx, rx) = tokio::sync::oneshot::channel(); app.once("unlocked", move |_| { let _ = tx.send(()); }); let timeout = Duration::from_secs(60); tokio::select! { // if the frontend is unlocked within 60 seconds, release visibility lock and proceed _ = rx => lease.release(), // otherwise, dump this request, but return Ok so we don't get an error popup _ = sleep(timeout) => { state.unregister_terminal_request().await; eprintln!("WARNING: Request to launch terminal timed out after 60 seconds."); return Ok(()); }, } } // session should really be unlocked at this point, but if the frontend misbehaves // (i.e. lies about unlocking) we could end up here with a locked session // this will result in an error popup to the user (see main hotkey handler) if use_base { let base_creds = state.get_aws_base("default").await?; cmd.env("AWS_ACCESS_KEY_ID", &base_creds.access_key_id); cmd.env("AWS_SECRET_ACCESS_KEY", &base_creds.secret_access_key); } else { let session_creds = state.get_aws_session("default").await?; cmd.env("AWS_ACCESS_KEY_ID", &session_creds.access_key_id); cmd.env("AWS_SECRET_ACCESS_KEY", &session_creds.secret_access_key); cmd.env("AWS_SESSION_TOKEN", &session_creds.session_token); } let res = match cmd.spawn() { Ok(_) => Ok(()), Err(e) if std::io::ErrorKind::NotFound == e.kind() => { Err(ExecError::NotFound(cmd.get_program().to_owned())) }, Err(e) => Err(ExecError::ExecutionFailed(e)), }; state.unregister_terminal_request().await; res?; // ? auto-conversion is more liberal than .into() Ok(()) }