feat(spaceshd): config terminal+appearance sections and save
This commit is contained in:
@@ -4,14 +4,34 @@
|
||||
//! partial config never breaks startup.
|
||||
|
||||
use std::path::Path;
|
||||
use serde::Deserialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize)]
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct TerminalConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub font_family: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub font_size: Option<u16>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct AppearanceConfig {
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub theme: Option<String>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub accent: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
pub struct Config {
|
||||
/// Shell launched for plain (no-command) panels. When unset, the daemon
|
||||
/// auto-detects the user's login shell.
|
||||
#[serde(default)]
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub default_shell: Option<String>,
|
||||
#[serde(default)]
|
||||
pub terminal: TerminalConfig,
|
||||
#[serde(default)]
|
||||
pub appearance: AppearanceConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -27,6 +47,23 @@ impl Config {
|
||||
Err(_) => Self::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Persist to `~/.spacesh/config.toml`.
|
||||
pub fn save(&self) -> std::io::Result<()> {
|
||||
let dir = crate::lifecycle::spacesh_dir()
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
|
||||
self.save_to(&dir.join("config.toml"))
|
||||
}
|
||||
|
||||
/// Persist to an arbitrary path. Creates the parent directory if needed.
|
||||
pub fn save_to(&self, path: &Path) -> std::io::Result<()> {
|
||||
if let Some(parent) = path.parent() {
|
||||
std::fs::create_dir_all(parent)?;
|
||||
}
|
||||
let s = toml::to_string_pretty(self)
|
||||
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
|
||||
std::fs::write(path, s)
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve the shell to spawn for a plain panel.
|
||||
@@ -98,4 +135,41 @@ mod tests {
|
||||
std::env::remove_var("SPACESH_SHELL");
|
||||
assert_eq!(s, "/tmp/fake-shell");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_terminal_and_appearance() {
|
||||
let dir = std::env::temp_dir().join(format!("spacesh-cfg-sections-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("config.toml");
|
||||
std::fs::write(&path,
|
||||
"default_shell = \"/bin/zsh\"\n[terminal]\nfont_family = \"Menlo\"\nfont_size = 15\n[appearance]\ntheme = \"light\"\naccent = \"teal\"\n").unwrap();
|
||||
let c = Config::from_path(&path);
|
||||
assert_eq!(c.terminal.font_family.as_deref(), Some("Menlo"));
|
||||
assert_eq!(c.terminal.font_size, Some(15));
|
||||
assert_eq!(c.appearance.theme.as_deref(), Some("light"));
|
||||
assert_eq!(c.appearance.accent.as_deref(), Some("teal"));
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_sections_default() {
|
||||
let c = Config::from_path(Path::new("/no/such/cfg.toml"));
|
||||
assert!(c.terminal.font_family.is_none());
|
||||
assert!(c.appearance.theme.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn save_then_reload_round_trips() {
|
||||
let dir = std::env::temp_dir().join(format!("spacesh-cfg-save-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("config.toml");
|
||||
let mut c = Config::default();
|
||||
c.terminal.font_size = Some(14);
|
||||
c.appearance.accent = Some("purple".into());
|
||||
c.save_to(&path).unwrap();
|
||||
let back = Config::from_path(&path);
|
||||
assert_eq!(back.terminal.font_size, Some(14));
|
||||
assert_eq!(back.appearance.accent.as_deref(), Some("purple"));
|
||||
let _ = std::fs::remove_file(&path);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user