usable backend for terminal launch
This commit is contained in:
parent
89bc74e644
commit
890f715388
@ -1,4 +1,5 @@
|
|||||||
use std::net::Ipv4Addr;
|
use std::net::Ipv4Addr;
|
||||||
|
use std::ffi::OsString;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use auto_launch::AutoLaunchBuilder;
|
use auto_launch::AutoLaunchBuilder;
|
||||||
@ -15,7 +16,7 @@ pub struct TermConfig {
|
|||||||
// we call it exec because it isn't always the actual path,
|
// we call it exec because it isn't always the actual path,
|
||||||
// in some cases it's just the name and relies on path-searching
|
// in some cases it's just the name and relies on path-searching
|
||||||
pub exec: PathBuf,
|
pub exec: PathBuf,
|
||||||
pub args: Vec<String>,
|
pub args: Vec<OsString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -31,7 +32,8 @@ pub struct AppConfig {
|
|||||||
pub start_minimized: bool,
|
pub start_minimized: bool,
|
||||||
#[serde(default = "default_start_on_login")]
|
#[serde(default = "default_start_on_login")]
|
||||||
pub start_on_login: bool,
|
pub start_on_login: bool,
|
||||||
pub terminal: Option<TermConfig>,
|
#[serde(default = "default_term_config")]
|
||||||
|
pub terminal: TermConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -43,7 +45,7 @@ impl Default for AppConfig {
|
|||||||
rehide_ms: default_rehide_ms(),
|
rehide_ms: default_rehide_ms(),
|
||||||
start_minimized: default_start_minimized(),
|
start_minimized: default_start_minimized(),
|
||||||
start_on_login: default_start_on_login(),
|
start_on_login: default_start_on_login(),
|
||||||
terminal: None,
|
terminal: default_term_config(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,6 +130,44 @@ fn default_listen_port() -> u16 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn default_term_config() -> TermConfig {
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
if let Ok(path) = which::which("pwsh.exe") {
|
||||||
|
return TermConfig {
|
||||||
|
name: exe.into(),
|
||||||
|
exec: "conhost.exe".into(),
|
||||||
|
args: vec![path.into_os_string()]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return TermConfig {
|
||||||
|
name: "powershell.exe".into(),
|
||||||
|
exec: "conhost.exe".into(),
|
||||||
|
args: vec!["powershell.exe".into()]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
{
|
||||||
|
for bin in ["gnome-terminal", "konsole"] {
|
||||||
|
if let Ok(_) = which::which(bin) {
|
||||||
|
return TermConfig {
|
||||||
|
name: bin.into(),
|
||||||
|
exec: bin.into(),
|
||||||
|
args: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return TermConfig {
|
||||||
|
name: "gnome-terminal".into(),
|
||||||
|
exec: "gnome-terminal".into(),
|
||||||
|
args: vec![],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
fn default_listen_addr() -> Ipv4Addr { Ipv4Addr::LOCALHOST }
|
fn default_listen_addr() -> Ipv4Addr { Ipv4Addr::LOCALHOST }
|
||||||
fn default_rehide_ms() -> u64 { 1000 }
|
fn default_rehide_ms() -> u64 { 1000 }
|
||||||
// start minimized and on login only in production mode
|
// start minimized and on login only in production mode
|
||||||
|
@ -81,6 +81,16 @@ impl Session {
|
|||||||
Session::Empty => Err(GetSessionError::CredentialsEmpty),
|
Session::Empty => Err(GetSessionError::CredentialsEmpty),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn try_get(
|
||||||
|
&self
|
||||||
|
) -> Result<(&BaseCredentials, &SessionCredentials), GetCredentialsError> {
|
||||||
|
match self {
|
||||||
|
Self::Empty => Err(GetCredentialsError::Empty),
|
||||||
|
Self::Locked(_) => Err(GetCredentialsError::Locked),
|
||||||
|
Self::Unlocked{ ref base, ref session } => Ok((base, session))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::convert::AsRef;
|
use std::convert::AsRef;
|
||||||
|
use std::ffi::OsString;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use strum_macros::AsRefStr;
|
use strum_macros::AsRefStr;
|
||||||
|
|
||||||
@ -216,7 +217,7 @@ pub enum RequestError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Errors encountered while running a subprocess (via creddy exec)
|
// Errors encountered while running a subprocess via creddy exec
|
||||||
#[derive(Debug, ThisError, AsRefStr)]
|
#[derive(Debug, ThisError, AsRefStr)]
|
||||||
pub enum ExecError {
|
pub enum ExecError {
|
||||||
#[error("Please specify a command")]
|
#[error("Please specify a command")]
|
||||||
@ -237,6 +238,18 @@ pub enum CliError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Errors encountered while trying to launch a child process
|
||||||
|
#[derive(Debug, ThisError, AsRefStr)]
|
||||||
|
pub enum LaunchError {
|
||||||
|
#[error("Executable not found: {0:?}")]
|
||||||
|
ExeNotFound(OsString),
|
||||||
|
#[error("Failed to execute command: {0}")]
|
||||||
|
ExecutionFailed(#[from] std::io::Error),
|
||||||
|
#[error(transparent)]
|
||||||
|
GetCredentials(#[from] GetCredentialsError),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// =========================
|
// =========================
|
||||||
// Serialize implementations
|
// Serialize implementations
|
||||||
// =========================
|
// =========================
|
||||||
@ -327,3 +340,18 @@ impl Serialize for UnlockError {
|
|||||||
map.end()
|
map.end()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Serialize for LaunchError {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
|
||||||
|
let mut map = serializer.serialize_map(None)?;
|
||||||
|
map.serialize_entry("code", self.as_ref())?;
|
||||||
|
map.serialize_entry("msg", &format!("{self}"))?;
|
||||||
|
|
||||||
|
match self {
|
||||||
|
LaunchError::GetCredentials(src) => map.serialize_entry("source", &src)?,
|
||||||
|
_ => serialize_upstream_err(self, &mut map)?,
|
||||||
|
}
|
||||||
|
map.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -82,9 +82,6 @@ pub async fn save_config(config: AppConfig, app_state: State<'_, AppState>) -> R
|
|||||||
|
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn launch_terminal(base: bool) -> bool {
|
pub async fn launch_terminal(base: bool) -> Result<(), LaunchError> {
|
||||||
match terminal::launch(base).await {
|
terminal::launch(base).await
|
||||||
Ok(_) => true,
|
|
||||||
Err(_) => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,39 +3,25 @@ use std::process::Command;
|
|||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
use crate::app::APP;
|
use crate::app::APP;
|
||||||
use crate::config::TermConfig;
|
|
||||||
use crate::credentials::Session;
|
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::state::AppState;
|
use crate::state::AppState;
|
||||||
|
|
||||||
|
|
||||||
pub async fn launch(use_base: bool) -> Result<(), ExecError> {
|
pub async fn launch(use_base: bool) -> Result<(), LaunchError> {
|
||||||
// we may have multiple candidates, because we might be on unix and
|
|
||||||
// we don't have a good way of detecting for sure what default to use
|
|
||||||
let state = APP.get().unwrap().state::<AppState>();
|
let state = APP.get().unwrap().state::<AppState>();
|
||||||
let config = state.config.read().await;
|
|
||||||
let _ = match config.terminal {
|
|
||||||
Some(ref term) => launch_term(term, use_base).await,
|
|
||||||
None => launch_default(use_base).await,
|
|
||||||
}?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async fn launch_term(term: &TermConfig, use_base: bool) -> Result<(), std::io::Error> {
|
|
||||||
// do all this in a block so we don't hold the lock any longer than necessary
|
// do all this in a block so we don't hold the lock any longer than necessary
|
||||||
let mut cmd = Command::new(&term.exec);
|
let mut cmd = {
|
||||||
cmd.args(&term.args);
|
let config = state.config.read().await;
|
||||||
|
let mut cmd = Command::new(&config.terminal.exec);
|
||||||
|
cmd.args(&config.terminal.args);
|
||||||
|
cmd
|
||||||
|
};
|
||||||
|
|
||||||
|
// similarly
|
||||||
{
|
{
|
||||||
// note: if called from launch(), there is already a lock being held on state.config
|
|
||||||
// don't read that here or we will deadlock
|
|
||||||
let state = APP.get().unwrap().state::<AppState>();
|
let state = APP.get().unwrap().state::<AppState>();
|
||||||
let app_session = state.session.read().await;
|
let app_session = state.session.read().await;
|
||||||
let (base_creds, session_creds) = match *app_session {
|
let (base_creds, session_creds) = app_session.try_get()?;
|
||||||
Session::Locked(_) | Session::Empty => todo!(),
|
|
||||||
Session::Unlocked{ref base, ref session} => (base, session),
|
|
||||||
};
|
|
||||||
|
|
||||||
if use_base {
|
if use_base {
|
||||||
cmd.env("AWS_ACCESS_KEY_ID", &base_creds.access_key_id);
|
cmd.env("AWS_ACCESS_KEY_ID", &base_creds.access_key_id);
|
||||||
cmd.env("AWS_SECRET_ACCESS_KEY", &base_creds.secret_access_key);
|
cmd.env("AWS_SECRET_ACCESS_KEY", &base_creds.secret_access_key);
|
||||||
@ -47,51 +33,16 @@ async fn launch_term(term: &TermConfig, use_base: bool) -> Result<(), std::io::E
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = cmd.spawn()?;
|
match cmd.spawn() {
|
||||||
Ok(())
|
Ok(_) => Ok(()),
|
||||||
}
|
Err(e) => {
|
||||||
|
use std::io::ErrorKind::*;
|
||||||
|
if let NotFound = e.kind() {
|
||||||
async fn launch_default(use_base: bool) -> Result<(), std::io::Error> {
|
Err(LaunchError::ExeNotFound(cmd.get_program().to_owned()))
|
||||||
let defaults = default_terms();
|
}
|
||||||
let last_idx = defaults.len() - 1;
|
else {
|
||||||
for (i, candidate) in defaults.iter().enumerate() {
|
Err(LaunchError::from(e))
|
||||||
match launch_term(candidate, use_base).await {
|
|
||||||
Ok(_) => return Ok(()),
|
|
||||||
Err(e) => {
|
|
||||||
if std::io::ErrorKind::NotFound == e.kind() && i < last_idx {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return Err(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// we only continue the loop if there are further iterations left, so this is safe
|
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn default_terms() -> Vec<TermConfig> {
|
|
||||||
#[cfg(windows)]
|
|
||||||
return vec![
|
|
||||||
TermConfig {
|
|
||||||
name: "powershell.exe".into(),
|
|
||||||
exec: "conhost.exe".into(),
|
|
||||||
args: vec!["powershell.exe".into()]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
return vec![
|
|
||||||
TermConfig {
|
|
||||||
name: "gnome-terminal".into(),
|
|
||||||
exec: "gnome-terminal".into(),
|
|
||||||
args: vec![],
|
|
||||||
},
|
|
||||||
TermConfig {
|
|
||||||
name: "konsole".into(),
|
|
||||||
exec: "konsole".into(),
|
|
||||||
args: vec![],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user