mirror of
https://codeberg.org/unspeaker/tek.git
synced 2026-07-17 07:46:57 +02:00
117 lines
4.1 KiB
Rust
117 lines
4.1 KiB
Rust
use crate::*;
|
|
|
|
/// Configuration: mode, view, and bind definitions.
|
|
///
|
|
/// ```
|
|
/// let config = tek::Config::default();
|
|
/// ```
|
|
///
|
|
/// ```
|
|
/// // Some dizzle.
|
|
/// // What indentation to use here lol?
|
|
/// let source = stringify!((mode :menu (name Menu)
|
|
/// (info Mode selector.) (keys :axis/y :confirm)
|
|
/// (view (bg (g 0) (bsp/s :ports/out
|
|
/// (bsp/n :ports/in
|
|
/// (bg (g 30) (bsp/s (fixed/y 7 :logo)
|
|
/// (fill :dialog/menu)))))))));
|
|
/// // Add this definition to the config and try to load it.
|
|
/// // A "mode" is basically a state machine
|
|
/// // with associated input and output definitions.
|
|
/// tek::Config::default().add(&source).unwrap().get_mode(":menu").unwrap();
|
|
/// ```
|
|
#[derive(Default, Debug)] pub struct Config {
|
|
/// XDG base directories of running user.
|
|
pub dirs: BaseDirectories,
|
|
/// Active collection of interaction modes.
|
|
pub modes: Modes,
|
|
/// Active collection of event bindings.
|
|
pub binds: Binds,
|
|
/// Active collection of view definitions.
|
|
pub views: Views,
|
|
}
|
|
|
|
impl Config {
|
|
const CONFIG_DIR: &'static str = "tek";
|
|
const CONFIG_SUB: &'static str = "v0";
|
|
const CONFIG: &'static str = "tek.edn";
|
|
const DEFAULTS: &'static str = include_str!("../tek.edn");
|
|
|
|
pub fn watch <T> (callback: impl FnOnce(Self)->T) -> Usually<T> {
|
|
let config = Self::init_new(None)?;
|
|
let result = callback(config);
|
|
Ok(result)
|
|
}
|
|
|
|
pub fn init_new (dirs: Option<BaseDirectories>) -> Usually<Self> {
|
|
let mut config = Self::new(None);
|
|
config.init()?;
|
|
Ok(config)
|
|
}
|
|
|
|
/// Create a new app configuration from a set of XDG base directories,
|
|
pub fn new (dirs: Option<BaseDirectories>) -> Self {
|
|
let default = ||BaseDirectories::with_profile(Self::CONFIG_DIR, Self::CONFIG_SUB);
|
|
let dirs = dirs.unwrap_or_else(default);
|
|
Self { dirs, ..Default::default() }
|
|
}
|
|
|
|
/// Write initial contents of configuration.
|
|
pub fn init (&mut self) -> Usually<()> {
|
|
self.init_one(Self::CONFIG, Self::DEFAULTS, |cfgs, dsl|{
|
|
cfgs.add(&dsl)?;
|
|
Ok(())
|
|
})?;
|
|
Ok(())
|
|
}
|
|
|
|
/// Write initial contents of a configuration file.
|
|
pub fn init_one (
|
|
&mut self, path: &str, defaults: &str, mut each: impl FnMut(&mut Self, &str)->Usually<()>
|
|
) -> Usually<()> {
|
|
if self.dirs.find_config_file(path).is_none() {
|
|
//println!("Creating {path:?}");
|
|
std::fs::write(self.dirs.place_config_file(path)?, defaults)?;
|
|
}
|
|
Ok(if let Some(path) = self.dirs.find_config_file(path) {
|
|
//println!("Loading {path:?}");
|
|
let src = std::fs::read_to_string(&path)?;
|
|
src.as_str().each(move|item|each(self, item))?;
|
|
} else {
|
|
return Err(format!("{path}: not found").into())
|
|
})
|
|
}
|
|
|
|
/// Add statements to configuration from [Dsl] source.
|
|
pub fn add (&mut self, dsl: impl Language) -> Usually<&mut Self> {
|
|
dsl.each(|item|self.add_one(item))?;
|
|
Ok(self)
|
|
}
|
|
|
|
fn add_one (&self, item: impl Language) -> Usually<()> {
|
|
if let Some(expr) = item.expr()? {
|
|
let head = expr.head()?;
|
|
let tail = expr.tail()?;
|
|
let name = tail.head()?;
|
|
let body = tail.tail()?;
|
|
//println!("Config::load: {} {} {}", head.unwrap_or_default(), name.unwrap_or_default(), body.unwrap_or_default());
|
|
match head {
|
|
Some("mode") if let Some(name) = name => self.modes.add(&name, &body)?,
|
|
Some("keys") if let Some(name) = name => load_bind(&self.binds, &name, &body)?,
|
|
Some("view") if let Some(name) = name => load_view(&self.views, &name, &body)?,
|
|
_ => return Err(format!("Config::load: expected view/keys/mode, got: {item:?}").into())
|
|
}
|
|
Ok(())
|
|
} else {
|
|
return Err(format!("Config::load: expected expr, got: {item:?}").into())
|
|
}
|
|
}
|
|
|
|
pub fn get_mode (&self, mode: impl AsRef<str>) -> Option<Arc<Mode<Arc<str>>>> {
|
|
self.modes.get(mode)
|
|
}
|
|
}
|
|
|
|
mod views; pub use self::views::*;
|
|
mod modes; pub use self::modes::*;
|
|
mod mode; pub use self::mode::*;
|