fix config loading

This commit is contained in:
i do not exist 2026-08-23 06:12:01 +03:00
parent a126d98f3c
commit 9e25564abb
4 changed files with 116 additions and 123 deletions

View file

@ -2,55 +2,88 @@ use crate::*;
use std::path::PathBuf;
use notify_debouncer_full::notify::{RecursiveMode, PollWatcher, Config as NotifyConfig, Watcher};
/// Write initial contents of configuration.
pub fn config_init (config: &Config) -> UsuallyRef<'static, ()> {
//println!("\r\ninit {}", quanta::Clock::new().raw());
config.clear();
let path = Config::CONFIG;
let defaults = Config::DEFAULTS;
config.stamp.store(quanta::Clock::new().raw(), Relaxed);
if config.find_file().is_none() {
//println!("Creating {path:?}");
std::fs::write(config.place_file()?, defaults)?;
impl AsRef<Config> for Config {
fn as_ref (&self) -> &Config {
self
}
Ok(if let Some(path) = config.find_file() {
//println!("Loading {path:?}");
let src = std::fs::read_to_string(&path)?;
src.as_str().each((), &mut move|ctx, item: &str|{
config_add(config, item);
Ok(ctx)
});
}
/// Write initial contents of configuration.
pub fn config_init <C: AsRef<Config>> (config: C) -> Usually<C> {
if config.as_ref().find_file().is_none() {
std::fs::write(config.as_ref().place_file()?, Config::DEFAULTS)?;
}
Ok(if let Some(path) = config.as_ref().find_file() {
config_load(config, std::fs::read_to_string(&path)?)?
} else {
return Err(format!("{path}: not found").into())
return Err(format!("config_init: not found").into())
})
}
/// Add statements to configuration from [Dsl] source.
pub fn config_add <'a> (config: &'a Config, dsl: &'a str) -> UsuallyRef<'a, &'a Config> {
dsl.each(config, &mut move|ctx: &'a Config, item: &'a str|if let Some(expr) = item.expr()? {
//if let (Some(name), Some(body)) = (expr.tail()?.head()?, expr.tail()?.tail()?,) {
match expr.head()? {
Some("mode") => expr.tail()?.map(|tail|modes_add(&config.modes, tail)),
Some("keys") => expr.tail()?.map(|tail|load_bind(&config.binds, tail)),
Some("view") => expr.tail()?.map(|tail|load_view(&config.views, tail)),
_ => return Err(format!("Config::load: expected view/keys/mode expr, got: {item:?}").into())
}.transpose()?;
//}
Ok(config)
pub fn config_load <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
config.as_ref().clear();
config.as_ref().stamp.store(quanta::Clock::new().raw(), Relaxed);
src.each(config, |c, s|config_load_item(c, s))
}
pub fn config_load_item <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
if let Some(expr) = src.expr()? {
config_load_kind(config, expr)
} else {
Err(format!("Config::add_one: tried to add empty item").into())
})
}
}
pub fn config_load_kind <C: AsRef<Config>, L: Language> (config: C, src: L) -> Usually<C> {
//if let Some(tail) = src.tail()? {
match src.head()? {
Some("mode") => modes_add(&config.as_ref().modes, src.tail()),
Some("keys") => load_bind(&config.as_ref().binds, src.tail()),
Some("view") => load_view(&config.as_ref().views, src.tail()),
_ => return Err(format!("Config::load: expected view/keys/mode expr, got: {src:?}").into())
}?;
//}
Ok(config)
}
/// Watch a config's file for changes.
pub fn config_watch (
config: Arc<Config>, poll: Option<Duration>
) -> Usually<()> {
let poll = poll.unwrap_or(Duration::from_millis(250));
let opts = NotifyConfig::default().with_poll_interval(poll);
let mut watcher = ::notify_debouncer_full::notify::poll::PollWatcher::new({
let config = config.clone();
move|result|{
match result {
Ok(_events) => if let Err(e) = config_init(config.as_ref()) {
*config.as_ref().error.write().unwrap() = Some(format!("{e:?}").into());
panic!("{e:?}");
} else {
//println!("config updated");
},
Err(errors) => {
panic!("{errors:?}");
}
}
}
}, opts)?;
if let Some(path) = config.as_ref().get_file() {
//println!("watching: {path:?}");
watcher.watch(&path, RecursiveMode::NonRecursive)?;
*config.as_ref().watch.write().unwrap() = Some(watcher);
Ok(())
} else {
Err(format!("no config path").into())
}
}
/// Register a mode.
pub fn modes_add <'a> (modes: &Modes, expr: &'a str) -> UsuallyRef<'a, ()> {
pub fn modes_add <'a> (modes: &Modes, expr: impl Language) -> UsuallyRef<'a, ()> {
let name = expr.head()?.ok_or("mode: missing name")?;
let body = expr.tail()?.ok_or("mode: missing body")?;
let mode = Mode::default();
let mode = body.each(mode, move|mut submode: Mode, item: &str|{
mode_add(&mut submode, &item);
Ok(submode)
})?;
let mode = body.each(mode, |c,s|mode_add(c,s))?;
modes.0.write().unwrap().insert(name.into(), Arc::new(mode));
Ok(())
}
@ -69,8 +102,8 @@ pub fn modes_add <'a> (modes: &Modes, expr: &'a str) -> UsuallyRef<'a, ()> {
/// let mut mode: tek::Mode<std::sync::Arc<str>> = Default::default();
/// mode.add("(name hello)").unwrap();
/// ```
pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mut Mode> {
Ok(Some(if let Ok(Some(expr)) = dsl.expr() && let Ok(Some(head)) = expr.head() {
pub fn mode_add (mut mode: Mode, dsl: impl Language) -> Usually<Mode> {
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 {
@ -78,16 +111,13 @@ pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mu
let name = tail.head()?.ok_or("submode: missing name")?;
let body = tail.tail()?.ok_or("submode: missing body")?;
let submode = Mode::default();
let submode = body.each(submode, move|mut submode: Mode, item: &str|{
mode_add(&mut submode, &item);
Ok(submode)
})?;
let submode = body.each(submode, |c,s|mode_add(c,s))?;
let modes = mode.modes.clone();
modes.0.write().unwrap().insert(name.into(), Arc::new(submode));
mode
},
"keys" => {
dsl.each(mode, &mut |mode: &'a mut Mode, expr: &'a str|{
dsl.each(mode, |mut mode: Mode, expr: &str|{
mode.keys.push(expr.trim().into());
Ok(mode)
})?
@ -102,11 +132,11 @@ pub fn mode_add <'a> (mode: &'a mut Mode, dsl: &'a str) -> PerhapsRef<'a, &'a mu
mode
} else {
return Err(format!("Mode::add: unexpected: {dsl:?}").into());
}))
})
}
/// Load custom view definition.
pub fn load_view <'a> (views: &Views, expr: &'a str,) -> UsuallyRef<'a, ()> {
pub fn load_view <'a> (views: &Views, expr: impl Language) -> UsuallyRef<'a, ()> {
let name = expr.head()?.ok_or("view: missing name")?;
let body = expr.tail()?.ok_or("view: missing body")?;
views.write().unwrap().insert(
@ -116,12 +146,12 @@ pub fn load_view <'a> (views: &Views, expr: &'a str,) -> UsuallyRef<'a, ()> {
Ok(())
}
pub fn load_bind <'a> (binds: &Binds, expr: &'a str) -> UsuallyRef<'a, ()> {
pub fn load_bind <'a> (binds: &Binds, expr: impl Language) -> UsuallyRef<'a, ()> {
let name = expr.head()?.ok_or("bind: missing name")?;
let body = expr.tail()?.ok_or("bind: missing body")?;
binds.write().unwrap().insert(name.into(), {
let mut map = Bind::new();
body.each((), &mut |_, item: &str|if item.expr().head() == Ok(Some("see")) {
body.each((), |_, item: &str|if item.expr().head() == Ok(Some("see")) {
// TODO
Ok(())
} else if let Ok(Some(_word)) = item.expr().head().word() {
@ -241,47 +271,14 @@ impl Config {
/// Create, initialize, and watch a new configuration.
pub fn watched <T: 'static> (callback: impl FnOnce(Arc<Config>)->T) -> Usually<T> {
let config = Self::init_new(None)?;
Self::watch(config.clone(), None)?;
config_watch(config.clone(), None)?;
let result = callback(config);
Ok(result)
}
/// Create and initialize a new configuration.
pub fn init_new (_dirs: Option<BaseDirectories>) -> UsuallyRef<'static, Arc<Self>> {
let config = Arc::new(Self::new(None));
config_init(&config)?;
Ok(config)
}
/// Watch a config's file for changes.
pub fn watch (config: Arc<Config>, poll: Option<Duration>) -> UsuallyRef<'static, ()> {
let handler = {
let config = config.clone();
move |result|match result {
Ok(_events) => if let Err(e) = config_init(&config) {
*config.error.write().unwrap() = Some(format!("{e:?}").into());
panic!("{e:?}");
} else {
//println!("config updated");
},
Err(errors) => {
panic!("{errors:?}");
}
}
};
let mut watcher = ::notify_debouncer_full::notify::poll::PollWatcher::new(
handler,
NotifyConfig::default()
.with_poll_interval(poll.unwrap_or(Duration::from_millis(250)))
)?;
if let Some(path) = config.get_file() {
//println!("watching: {path:?}");
watcher.watch(&path, RecursiveMode::NonRecursive)?;
*config.watch.write().unwrap() = Some(watcher);
Ok(())
} else {
Err(format!("no config path").into())
}
pub fn init_new (_dirs: Option<BaseDirectories>) -> Usually<Arc<Self>> {
config_init(Arc::new(Self::new(None)))
}
/// Find the config file.
@ -316,6 +313,10 @@ impl Config {
*self.binds.write().unwrap() = Default::default();
}
pub fn get_view (&self, name: impl AsRef<str>) -> Option<Arc<str>> {
self.views.read().unwrap().get(name.as_ref()).cloned()
}
}
pub use self::view::*;