wip: unify default config
Some checks are pending
/ build (push) Waiting to run

This commit is contained in:
🪞👃🪞 2025-08-10 16:13:54 +03:00
parent 4d4c470a81
commit 50728729b7
8 changed files with 232 additions and 335 deletions

View file

@ -54,10 +54,10 @@ pub struct App {
pub size: Measure<TuiOut>,
/// Performance counter
pub perf: PerfModel,
/// Available view profiles and input bindings
/// Available view modes and input bindings
pub config: Config,
/// Currently selected profile
pub profile: Profile,
/// Currently selected mode
pub mode: Mode<Arc<str>>,
/// Contains the currently edited musical arrangement
pub project: Arrangement,
/// Contains all recently created clips.
@ -72,124 +72,121 @@ pub struct App {
/// Configuration
#[derive(Default, Debug)]
pub struct Config {
/// XDG basedirs
pub dirs: BaseDirectories,
/// Available view profiles
pub views: Arc<RwLock<BTreeMap<Arc<str>, Profile>>>,
/// Available input bindings
pub modes: Arc<RwLock<BTreeMap<Arc<str>, Arc<Mode<Arc<str>>>>>>,
pub binds: Arc<RwLock<BTreeMap<Arc<str>, EventMap<Option<TuiEvent>, Arc<str>>>>>,
}
/// Profile
#[derive(Default, Debug)]
pub struct Profile {
/// Path of configuration entrypoint
pub struct Mode<D: Dsl + Ord> {
pub path: PathBuf,
/// Default mode
pub mode: Mode,
/// Optional modes
pub modes: BTreeMap<Arc<str>, Mode>,
}
#[derive(Default, Debug)]
pub struct Mode {
/// Name of configuration
pub name: Vec<Arc<str>>,
/// Description of configuration
pub info: Vec<Arc<str>>,
/// View definition
pub view: Vec<Arc<str>>,
// Input keymap
pub keys: Vec<Arc<str>>,
pub name: Vec<D>,
pub info: Vec<D>,
pub view: Vec<D>,
pub keys: Vec<D>,
pub modes: BTreeMap<D, Mode<D>>,
}
impl Config {
const VIEWS: &'static str = "views.edn";
const BINDS: &'static str = "binds.edn";
const DEFAULT_VIEWS: &'static str = include_str!("../../config/views.edn");
const DEFAULT_BINDS: &'static str = include_str!("../../config/binds.edn");
const CONFIG: &'static str = "tek.edn";
const DEFAULTS: &'static str = include_str!("../../tek.edn");
pub fn init () -> Usually<Self> {
let mut cfgs: Self = Default::default();
cfgs.dirs = BaseDirectories::with_profile("tek", "v0");
cfgs.load(Self::BINDS, Self::DEFAULT_BINDS, |cfgs, dsl|cfgs.load_bind(dsl))?;
cfgs.load(Self::VIEWS, Self::DEFAULT_VIEWS, |cfgs, dsl|cfgs.load_view(dsl))?;
cfgs.init_file(Self::CONFIG, Self::DEFAULTS, |cfgs, dsl|cfgs.load_defs(dsl))?;
Ok(cfgs)
}
pub fn load (
pub fn init_file (
&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())
})
}
pub fn load_bind <D: Dsl> (&mut self, dsl: D) -> Usually<()> {
load_modules(dsl, |id, tail|Ok(self.binds.write().unwrap().insert(id.into(), {
let mut map = EventMap::new();
tail.each(|item|{
map.add(TuiEvent::from_dsl(item.exp()?.head()?)?, Binding {
command: item.exp()?.tail()?.unwrap_or_default().into(),
condition: None,
description: None,
source: None
});
Ok(())
})?;
map
})))
}
pub fn load_view <D: Dsl> (&mut self, dsl: D) -> Usually<()> {
load_modules(&dsl, |id, tail|Ok(self.views.write().unwrap().insert(id.into(), {
let mut profile = Profile::default();
tail.each(|item|Ok(if let Ok(Some(exp)) = item.exp() {
match exp.head()? {
Some("name") => profile.mode.name.push(
exp.tail()?.map(|x|x.trim()).unwrap_or("").into()
),
Some("info") => profile.mode.info.push(
exp.tail()?.map(|x|x.trim()).unwrap_or("").into()
),
Some("keys") => if let Some(tail) = exp.tail()? {
tail.each(|keys|Ok(profile.mode.keys.push(keys.trim().into())))?;
} else {
return Err(format!("load_view: empty keys: {exp}").into())
},
Some("mode") => if let (Some(name), Some(tail)) = (
exp.tail()?.head()?, exp.tail()?.tail()?,
) {
let mut mode: Mode = Default::default();
tail.each(|item|Ok(if let Ok(Some(exp)) = item.exp() {
pub fn load_defs <D: Dsl> (&mut self, dsl: D) -> Usually<()> {
dsl.each(|item|{
println!("{item:?}");
Ok(match item.exp().head() {
Ok(Some("keys")) if let Some(id) = item.exp().tail().head()? => {
self.binds.write().unwrap().insert(id.into(), {
let mut map = EventMap::new();
item.exp().tail().tail()?.each(|item|Ok({
if let Ok(Some(sym)) = item.exp().head().sym() {
map.add(TuiEvent::from_dsl(item.exp()?.head()?)?, Binding {
command: item.exp()?.tail()?.unwrap_or_default().into(),
condition: None,
description: None,
source: None
});
} else if item.exp().head() == Ok(Some("see")) {
// TODO
} else {
return Err(format!("load_defs: unexpected: {item:?}").into())
}
}))?;
map
});
},
Ok(Some("mode")) if let Some(id) = item.exp().tail().head()? => {
self.modes.write().unwrap().insert(id.into(), {
let mut mode = Mode::default();
item.exp().tail().tail()?.each(|item|Ok(if let Ok(Some(exp)) = item.exp() {
match exp.head()? {
Some("name") => mode.name.push(
exp.tail()?.map(|x|x.trim()).unwrap_or("").into()
),
Some("info") => mode.info.push(
exp.tail()?.map(|x|x.trim()).unwrap_or("").into()
),
Some("keys") => if let Some(tail) = exp.tail()? {
tail.each(|keys|Ok(mode.keys.push(keys.trim().into())))?;
} else {
return Err(format!("load_view: empty keys: {exp}").into())
},
_ => {
return Err(format!("load_view: unexpected in mode {name}: {item:?}").into())
}
Some("mode") => if let (Some(name), Some(tail)) = (
exp.tail()?.head()?, exp.tail()?.tail()?,
) {
let mut submode: Mode<Arc<str>> = Default::default();
tail.each(|item|Ok(if let Ok(Some(exp)) = item.exp() {
match exp.head()? {
Some("keys") => if let Some(tail) = exp.tail()? {
tail.each(|keys|Ok(mode.keys.push(keys.trim().into())))?;
} else {
return Err(format!("load_view: empty keys: {exp}").into())
},
_ => {
return Err(format!("load_view: unexpected in mode {name}: {item:?}").into())
}
}
} else if let Ok(Some(sym)) = item.sym() {
// TODO
} else {
return Err(format!("load_view: unexpected in mode {name}: {item:?}").into())
}))?;
mode.modes.insert(name.trim().into(), submode);
} else {
return Err(format!("load_view: empty mode: {exp}").into())
},
Some(_) => mode.view.push(exp.into()),
None => return Err(format!("load_view: empty: {exp}").into())
}
} else if let Ok(Some(sym)) = item.sym() {
// TODO
mode.view.push(sym.into());
} else {
return Err(format!("load_view: unexpected in mode {name}: {item:?}").into())
return Err(format!("load_view: unexpected: {dsl:?}").into())
}))?;
profile.modes.insert(name.trim().into(), mode);
} else {
return Err(format!("load_view: empty mode: {exp}").into())
},
Some(_) => profile.mode.view.push(exp.into()),
None => return Err(format!("load_view: empty: {exp}").into())
}
} else if let Ok(Some(sym)) = item.sym() {
profile.mode.view.push(sym.into());
} else {
return Err(format!("load_view: unexpected: {dsl:?}").into())
}))?;
profile
})))
mode.into()
});
},
_ => return Err(format!("load_defs: unexpected: {item:?}").into())
})
})
}
}
fn load_modules <D: Dsl, U> (dsl: D, cb: impl Fn(&str, &str)->Usually<U>) -> Usually<()> {

View file

@ -2,15 +2,15 @@ use crate::*;
#[derive(Debug)]
pub enum AppCommand {}
handle!(TuiIn:|self: App, input|{
panic!("wat: {:?}", self.profile);
for keys in self.profile.mode.keys.iter() {
panic!("wat: {:?}", self.mode);
for keys in self.mode.keys.iter() {
panic!("{keys} {:?}", self.config.binds.read().unwrap());
if let Some(binding) = self.config.binds.read().unwrap().get(keys.as_ref()) {
panic!("{binding:?}");
}
}
Ok(None)
//Ok(if let Some(binding) = self.profile.as_ref()
//Ok(if let Some(binding) = self.mode.as_ref()
//.map(|c|c.keys.dispatch(input.event())).flatten()
//{
//let binding = binding.clone();

View file

@ -16,12 +16,12 @@ pub const VIEW: DslNs<'static, DslCb> = DslNs(&[
(":view/ports/ins", |state|Box::new(Fill::x(Fixed::y(3,
Bsp::a(Fill::x(Align::w(" L AUDIO INS")), Bsp::a("MIDI INS", Fill::x(Align::e("AUDIO INS R ")))))))),
(":view/profiles", |state: &App|Box::new({
let views = state.config.views.clone();
let modes = state.config.modes.clone();
Stack::south(move|add: &mut dyn FnMut(&dyn Render<TuiOut>)|{
for (index, (id, profile)) in views.read().unwrap().iter().enumerate() {
for (index, (id, profile)) in modes.read().unwrap().iter().enumerate() {
let bg = if index == 0 { Rgb(64,64,64) } else { Rgb(32,32,32) };
let name = profile.mode.name.get(0).map(|x|x.as_ref()).unwrap_or("<no name>");
let info = profile.mode.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
let name = profile.name.get(0).map(|x|x.as_ref()).unwrap_or("<no name>");
let info = profile.info.get(0).map(|x|x.as_ref()).unwrap_or("<no info>");
add(&Fixed::y(3, Tui::bg(bg, Bsp::s(
Fill::x(Bsp::a(
Fill::x(Align::w(Tui::fg(Rgb(224,192,128), name))),