restruct: 92e

This commit is contained in:
i do not exist 2026-06-23 21:39:12 +03:00
parent 4ec2165e3d
commit ae347eeef7
31 changed files with 2924 additions and 2663 deletions

192
src/app/config.rs Normal file
View file

@ -0,0 +1,192 @@
use crate::{*, bind::*, view::*};
/// 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 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 => load_mode(&self.modes, &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.clone().read().unwrap().get(mode.as_ref()).cloned()
}
}
pub(crate) fn load_mode (modes: &Modes, name: &impl AsRef<str>, body: &impl Language) -> Usually<()> {
let mut mode = Mode::default();
body.each(|item|mode.add(item))?;
modes.write().unwrap().insert(name.as_ref().into(), Arc::new(mode));
Ok(())
}
/// Collection of interaction modes.
pub type Modes = Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>;
impl Mode<Arc<str>> {
/// Add a definition to the mode.
///
/// Supported definitions:
///
/// - (name ...) -> name
/// - (info ...) -> description
/// - (keys ...) -> key bindings
/// - (mode ...) -> submode
/// - ... -> view
///
/// ```
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
/// mode.add("(name hello)").unwrap();
/// ```
pub fn add (&mut self, dsl: impl Language) -> Usually<()> {
Ok(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
//println!("Mode::add: {head} {:?}", expr.tail());
let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
match head {
"name" => self.add_name(tail)?,
"info" => self.add_info(tail)?,
"keys" => self.add_keys(tail)?,
"mode" => self.add_mode(tail)?,
_ => self.add_view(tail)?,
};
} else if let Ok(Some(word)) = dsl.word() {
self.add_view(word);
} else {
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
})
//DslParse(dsl, ||Err(format!("Mode::add: unexpected: {dsl:?}").into()))
//.word(|word|self.add_view(word))
//.expr(|expr|expr.head(|head|{
////println!("Mode::add: {head} {:?}", expr.tail());
//let tail = expr.tail()?.map(|x|x.trim()).unwrap_or("");
//match head {
//"name" => self.add_name(tail),
//"info" => self.add_info(tail),
//"keys" => self.add_keys(tail)?,
//"mode" => self.add_mode(tail)?,
//_ => self.add_view(tail),
//};
//}))
}
fn add_name (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.name.push(src.into())))
}
fn add_info (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.info.push(src.into())))
}
fn add_view (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(dsl.src()?.map(|src|self.view.push(src.into())))
}
fn add_keys (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(Some(dsl.each(|expr|{ self.keys.push(expr.trim().into()); Ok(()) })?))
}
fn add_mode (&mut self, dsl: impl Language) -> Perhaps<()> {
Ok(Some(if let Some(id) = dsl.head()? {
load_mode(&self.modes, &id, &dsl.tail())?;
} else {
return Err(format!("Mode::add: self: incomplete: {dsl:?}").into());
}))
}
}
/// Group of view and keys definitions.
///
/// ```
/// let mode = tek::Mode::<std::sync::Arc<str>>::default();
/// ```
#[derive(Default, Debug)] pub struct Mode<D: Language + Ord> {
pub path: PathBuf,
pub name: Vec<D>,
pub info: Vec<D>,
pub view: Vec<D>,
pub keys: Vec<D>,
pub modes: Modes,
}