use std::collections::HashMap;
use std::path::PathBuf;
use serde::Deserialize;
#[derive(Debug, Clone, Default, Deserialize)]
struct BaseConfig {
#[serde(default)]
exec: Option<String>,
#[serde(default)]
bin: HashMap<String, String>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct OperationConfig {
#[serde(flatten)]
base: BaseConfig,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct PortalConfig {
#[serde(flatten)]
base: BaseConfig,
#[serde(flatten)]
pub operations: HashMap<String, OperationConfig>,
}
#[derive(Debug, Clone, Default, Deserialize)]
pub struct Config {
#[serde(flatten)]
base: BaseConfig,
#[serde(flatten)]
pub portals: HashMap<String, PortalConfig>,
}
fn detect_terminal() -> Option<String> {
let terminals = ["foot", "alacritty", "kitty", "wezterm", "ghostty", "xterm"];
for term in terminals {
if std::process::Command::new("which")
.arg(term)
.output()
.map(|o| o.status.success())
.unwrap_or(false)
{
return Some(term.to_string());
}
}
None
}
impl Config {
pub fn load() -> Self {
Self::config_path()
.and_then(|path| std::fs::read_to_string(&path).ok())
.and_then(|content| toml::from_str(&content).ok())
.unwrap_or_else(|| Self {
base: BaseConfig {
exec: detect_terminal(),
bin: HashMap::new(),
},
portals: HashMap::new(),
})
}
fn config_path() -> Option<PathBuf> {
dirs::config_dir().map(|p| p.join("portty/config.toml"))
}
pub fn resolve_exec(&self, portal: &str, operation: &str) -> Option<&str> {
let portal_cfg = self.portals.get(portal);
let op_exec = portal_cfg
.and_then(|p| p.operations.get(operation))
.and_then(|o| o.base.exec.as_deref());
let portal_exec = portal_cfg.and_then(|p| p.base.exec.as_deref());
op_exec
.or(portal_exec)
.or(self.base.exec.as_deref())
.filter(|s| !s.is_empty())
}
pub fn resolve_bin(&self, portal: &str, operation: &str) -> HashMap<String, String> {
let mut bin = self.base.bin.clone();
if let Some(portal_cfg) = self.portals.get(portal) {
bin.extend(portal_cfg.base.bin.clone());
if let Some(op_cfg) = portal_cfg.operations.get(operation) {
bin.extend(op_cfg.base.bin.clone());
}
}
bin
}
}