2022-12-21 21:42:12 +00:00
|
|
|
use std::net::Ipv4Addr;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
2023-04-27 21:24:08 +00:00
|
|
|
use auto_launch::AutoLaunchBuilder;
|
2023-05-07 04:59:24 +00:00
|
|
|
use is_terminal::IsTerminal;
|
2022-12-23 00:36:32 +00:00
|
|
|
use serde::{Serialize, Deserialize};
|
|
|
|
use sqlx::SqlitePool;
|
2023-09-09 14:29:57 +00:00
|
|
|
use tauri::{
|
|
|
|
AppHandle,
|
|
|
|
Manager,
|
|
|
|
GlobalShortcutManager,
|
|
|
|
async_runtime as rt,
|
|
|
|
};
|
2022-12-21 21:42:12 +00:00
|
|
|
|
2022-12-23 00:36:32 +00:00
|
|
|
use crate::errors::*;
|
|
|
|
|
|
|
|
|
2023-08-03 02:57:37 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
|
|
pub struct TermConfig {
|
|
|
|
pub name: String,
|
|
|
|
// 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
|
2023-09-09 13:30:19 +00:00
|
|
|
// it's a string because it can come from the frontend as json
|
|
|
|
pub exec: String,
|
|
|
|
pub args: Vec<String>,
|
2023-08-03 02:57:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-09 14:29:57 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)]
|
|
|
|
pub struct HotkeyConfig {
|
|
|
|
// tauri uses strings to represent keybinds, so we will as well
|
|
|
|
pub show_window: String,
|
|
|
|
pub launch_terminal: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-23 00:36:32 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
2022-12-21 21:42:12 +00:00
|
|
|
pub struct AppConfig {
|
2022-12-23 19:34:17 +00:00
|
|
|
#[serde(default = "default_listen_addr")]
|
2022-12-21 21:42:12 +00:00
|
|
|
pub listen_addr: Ipv4Addr,
|
2022-12-23 19:34:17 +00:00
|
|
|
#[serde(default = "default_listen_port")]
|
2022-12-21 21:42:12 +00:00
|
|
|
pub listen_port: u16,
|
2022-12-23 19:34:17 +00:00
|
|
|
#[serde(default = "default_rehide_ms")]
|
2022-12-23 00:36:32 +00:00
|
|
|
pub rehide_ms: u64,
|
2022-12-23 19:34:17 +00:00
|
|
|
#[serde(default = "default_start_minimized")]
|
|
|
|
pub start_minimized: bool,
|
2023-04-27 21:24:08 +00:00
|
|
|
#[serde(default = "default_start_on_login")]
|
|
|
|
pub start_on_login: bool,
|
2023-08-03 23:35:15 +00:00
|
|
|
#[serde(default = "default_term_config")]
|
|
|
|
pub terminal: TermConfig,
|
2023-09-09 14:29:57 +00:00
|
|
|
#[serde(default = "default_hotkey_config")]
|
|
|
|
pub hotkeys: HotkeyConfig,
|
2022-12-23 00:36:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-21 21:42:12 +00:00
|
|
|
impl Default for AppConfig {
|
|
|
|
fn default() -> Self {
|
2022-12-23 00:36:32 +00:00
|
|
|
AppConfig {
|
2022-12-23 19:34:17 +00:00
|
|
|
listen_addr: default_listen_addr(),
|
|
|
|
listen_port: default_listen_port(),
|
|
|
|
rehide_ms: default_rehide_ms(),
|
|
|
|
start_minimized: default_start_minimized(),
|
2023-04-27 21:24:08 +00:00
|
|
|
start_on_login: default_start_on_login(),
|
2023-08-03 23:35:15 +00:00
|
|
|
terminal: default_term_config(),
|
2023-09-09 14:29:57 +00:00
|
|
|
hotkeys: default_hotkey_config(),
|
2022-12-21 21:42:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-26 22:49:08 +00:00
|
|
|
impl AppConfig {
|
|
|
|
pub async fn load(pool: &SqlitePool) -> Result<AppConfig, SetupError> {
|
|
|
|
let res = sqlx::query!("SELECT * from config where name = 'main'")
|
|
|
|
.fetch_optional(pool)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let row = match res {
|
|
|
|
Some(row) => row,
|
|
|
|
None => return Ok(AppConfig::default()),
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(serde_json::from_str(&row.data)?)
|
|
|
|
}
|
2022-12-23 00:36:32 +00:00
|
|
|
|
2023-04-26 22:49:08 +00:00
|
|
|
pub async fn save(&self, pool: &SqlitePool) -> Result<(), sqlx::error::Error> {
|
|
|
|
let data = serde_json::to_string(self).unwrap();
|
|
|
|
sqlx::query(
|
|
|
|
"INSERT INTO config (name, data) VALUES ('main', ?)
|
|
|
|
ON CONFLICT (name) DO UPDATE SET data = ?"
|
|
|
|
)
|
|
|
|
.bind(&data)
|
|
|
|
.bind(&data)
|
|
|
|
.execute(pool)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2022-12-23 00:36:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-28 21:33:04 +00:00
|
|
|
pub fn set_auto_launch(is_configured: bool) -> Result<(), SetupError> {
|
2023-04-27 21:24:08 +00:00
|
|
|
let path_buf = std::env::current_exe()
|
|
|
|
.map_err(|e| auto_launch::Error::Io(e))?;
|
|
|
|
let path = path_buf
|
|
|
|
.to_string_lossy();
|
|
|
|
|
|
|
|
let auto = AutoLaunchBuilder::new()
|
|
|
|
.set_app_name("Creddy")
|
|
|
|
.set_app_path(&path)
|
2023-04-29 05:34:17 +00:00
|
|
|
.build()?;
|
2023-04-27 21:24:08 +00:00
|
|
|
|
2023-04-28 21:33:04 +00:00
|
|
|
let is_enabled = auto.is_enabled()?;
|
|
|
|
if is_configured && !is_enabled {
|
2023-04-29 05:34:17 +00:00
|
|
|
auto.enable()?;
|
2023-04-27 21:24:08 +00:00
|
|
|
}
|
2023-04-28 21:33:04 +00:00
|
|
|
else if !is_configured && is_enabled {
|
2023-04-29 05:34:17 +00:00
|
|
|
auto.disable()?;
|
2023-04-27 21:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-29 05:34:17 +00:00
|
|
|
pub fn get_or_create_db_path() -> Result<PathBuf, DataDirError> {
|
|
|
|
let mut path = dirs::data_dir()
|
|
|
|
.ok_or(DataDirError::NotFound)?;
|
2023-05-07 04:59:24 +00:00
|
|
|
path.push("Creddy");
|
2022-12-21 21:42:12 +00:00
|
|
|
|
2023-04-29 05:34:17 +00:00
|
|
|
std::fs::create_dir_all(&path)?;
|
2023-05-07 04:59:24 +00:00
|
|
|
if cfg!(debug_assertions) && std::io::stdout().is_terminal() {
|
|
|
|
path.push("creddy.dev.db");
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
path.push("creddy.db");
|
|
|
|
}
|
2023-04-29 05:34:17 +00:00
|
|
|
|
|
|
|
Ok(path)
|
2022-12-23 00:36:32 +00:00
|
|
|
}
|
2022-12-23 19:34:17 +00:00
|
|
|
|
|
|
|
|
|
|
|
fn default_listen_port() -> u16 {
|
|
|
|
if cfg!(debug_assertions) {
|
|
|
|
12_345
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
19_923
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-03 23:35:15 +00:00
|
|
|
|
|
|
|
fn default_term_config() -> TermConfig {
|
|
|
|
#[cfg(windows)]
|
|
|
|
{
|
2023-09-09 13:30:19 +00:00
|
|
|
let shell = if which::which("pwsh.exe").is_ok() {
|
|
|
|
"pwsh.exe".to_string()
|
2023-08-03 23:35:15 +00:00
|
|
|
}
|
2023-09-09 13:30:19 +00:00
|
|
|
else {
|
|
|
|
"powershell.exe".to_string()
|
|
|
|
};
|
|
|
|
|
|
|
|
let (exec, args) = if cfg!(debug_assertions) {
|
|
|
|
("conhost.exe".to_string(), vec![shell.clone()])
|
|
|
|
} else {
|
|
|
|
(shell.clone(), vec![])
|
2023-08-03 23:35:15 +00:00
|
|
|
};
|
2023-09-09 13:30:19 +00:00
|
|
|
|
|
|
|
TermConfig { name: shell, exec, args }
|
2023-08-03 23:35:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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![],
|
|
|
|
};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-09-09 14:29:57 +00:00
|
|
|
fn default_hotkey_config() -> HotkeyConfig {
|
|
|
|
HotkeyConfig {
|
|
|
|
show_window: "alt+shift+C".into(),
|
|
|
|
launch_terminal: "alt+shift+T".into(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// note: will panic if called before APP is set
|
|
|
|
pub fn register_hotkeys(hotkeys: &HotkeyConfig) -> tauri::Result<()> {
|
|
|
|
let app = crate::app::APP.get().unwrap();
|
|
|
|
|
|
|
|
let mut manager = app.global_shortcut_manager();
|
|
|
|
manager.unregister_all()?;
|
|
|
|
|
|
|
|
let h = app.app_handle();
|
|
|
|
manager.register(
|
|
|
|
&hotkeys.show_window,
|
|
|
|
move || {
|
|
|
|
h.get_window("main")
|
|
|
|
.map(|w| w.show().error_popup("Failed to show"))
|
|
|
|
.ok_or(HandlerError::NoMainWindow)
|
|
|
|
.error_popup("No main window");
|
|
|
|
},
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// register() doesn't take an async fn, so we have to use spawn
|
|
|
|
manager.register(
|
|
|
|
&hotkeys.launch_terminal,
|
|
|
|
|| {
|
|
|
|
rt::spawn(async {
|
|
|
|
crate::terminal::launch(false)
|
|
|
|
.await
|
|
|
|
.error_popup("Failed to launch");
|
|
|
|
});
|
|
|
|
}
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-23 19:34:17 +00:00
|
|
|
fn default_listen_addr() -> Ipv4Addr { Ipv4Addr::LOCALHOST }
|
|
|
|
fn default_rehide_ms() -> u64 { 1000 }
|
2023-04-27 21:24:08 +00:00
|
|
|
// start minimized and on login only in production mode
|
|
|
|
fn default_start_minimized() -> bool { !cfg!(debug_assertions) }
|
|
|
|
fn default_start_on_login() -> bool { !cfg!(debug_assertions) }
|