config: extract read_and_leak; almost done with layer-if

This commit is contained in:
🪞👃🪞 2025-05-02 23:50:16 +03:00
parent cd8d85bd97
commit d427dc409d
3 changed files with 105 additions and 44 deletions

View file

@ -1,8 +1,11 @@
use crate::*;
use std::path::PathBuf;
/// Configuration
#[derive(Default, Debug)]
pub struct Configuration {
/// Path of configuration entrypoint
pub path: PathBuf,
/// Name of configuration
pub name: Option<Arc<str>>,
/// Description of configuration
@ -14,20 +17,19 @@ pub struct Configuration {
}
impl Configuration {
pub fn from_file (path: &impl AsRef<Path>, _watch: bool) -> Usually<Self> {
Self::from_source(String::from_utf8(std::fs::read(path.as_ref())?)?)
}
pub fn from_source (source: impl AsRef<str> + 'static) -> Usually<Self> {
let source: Box<str> = source.as_ref().into();
let source: &'static str = Box::leak(source);
let [name, info, view, keys] = Self::parse(TokenIter::from(source.as_ref()))?;
pub fn new (path: &impl AsRef<Path>, _watch: bool) -> Usually<Self> {
let text = read_and_leak(path.as_ref())?;
let [name, info, view, keys] = Self::parse(TokenIter::from(text))?;
Ok(Self {
path: path.as_ref().into(),
info: info.map(Self::parse_info).flatten(),
name: name.map(Self::parse_name).flatten(),
view: Self::parse_view(view)?,
keys: Self::parse_keys(keys)?,
keys: Self::parse_keys(&path, keys)?,
})
}
fn parse (iter: TokenIter) -> Usually<[Option<TokenIter>;4]> {
let mut name: Option<TokenIter> = None;
let mut info: Option<TokenIter> = None;
@ -59,6 +61,7 @@ impl Configuration {
}
Ok([name, info, view, keys])
}
fn parse_info (mut iter: TokenIter) -> Option<Arc<str>> {
iter.next().and_then(|x|if let Value::Str(x) = x.value {
Some(x.into())
@ -66,6 +69,7 @@ impl Configuration {
None
})
}
fn parse_name (mut iter: TokenIter) -> Option<Arc<str>> {
iter.next().and_then(|x|if let Value::Str(x) = x.value {
Some(x.into())
@ -73,6 +77,7 @@ impl Configuration {
None
})
}
fn parse_view (iter: Option<TokenIter>) -> Usually<TokenIter> {
if let Some(view) = iter {
Ok(view)
@ -80,36 +85,89 @@ impl Configuration {
Err(format!("missing view definition").into())
}
}
fn parse_keys (iter: Option<TokenIter>)
fn parse_keys (base: &impl AsRef<Path>, iter: Option<TokenIter<'static>>)
-> Usually<InputMap<'static, Tek, TekCommand, TuiIn, TokenIter<'static>>>
{
if let Some(mut keys) = iter {
let mut map = InputMap::default();
while let Some(token) = keys.next() {
match token.value {
Value::Exp(_, mut exp) => {
let next = exp.next();
match next {
Some(Token { value: Value::Key(sym), .. }) => match sym {
"layer" => { todo!() },
"layer-if" => { todo!() },
_ => return Err(
format!("(e3) unexpected symbol {sym:?}").into()
)
}
_ => return Err(
format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into()
)
}
},
t => return Err(
format!("(e1) unexpected token {token:?}").into()
)
}
}
Ok(map)
} else {
if iter.is_none() {
return Err(format!("missing keys definition").into())
}
let mut keys = iter.unwrap();
let mut map = InputMap::default();
while let Some(token) = keys.next() {
if let Value::Exp(_, mut exp) = token.value {
let next = exp.next();
if let Some(Token { value: Value::Key(sym), .. }) = next {
match sym {
"layer" => {
let next = exp.next();
if let Some(Token { value: Value::Str(path), .. }) = next {
let path = base.as_ref().parent().unwrap().join(unquote(path));
if !std::fs::exists(&path)? {
return Err(format!("(e5) not found: {path:?}").into())
}
map.add_layer(read_and_leak(path)?.into());
} else {
return Err(format!("(e4) unexpected non-string {next:?}").into())
}
},
"layer-if" => {
let mut cond = None;
let next = exp.next();
if let Some(Token { value: Value::Sym(sym), .. }) = next {
cond = Some(leak(sym));
} else {
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
};
let next = exp.next();
if let Some(Token { value: Value::Str(path), .. }) = next {
let path = base.as_ref().parent().unwrap().join(unquote(path));
if !std::fs::exists(&path)? {
return Err(format!("(e5) not found: {path:?}").into())
}
print!("{path:?}...");
let keys = read_and_leak(path)?.into();
println!("ok");
let cond = cond.unwrap();
map.add_layer_if(
Box::new(|state|{
Context::get(state, &Value::Sym(cond)).unwrap_or(false)
}),
keys
);
} else {
return Err(format!("(e4) unexpected non-symbol {next:?}").into())
}
},
_ => return Err(format!("(e3) unexpected symbol {sym:?}").into())
}
} else {
return Err(format!("(e2) unexpected exp {:?}", next.map(|x|x.value)).into())
}
} else {
return Err(format!("(e1) unexpected token {token:?}").into())
}
}
Ok(map)
}
}
fn read_and_leak (path: impl AsRef<Path>) -> Usually<&'static str> {
Ok(leak(String::from_utf8(std::fs::read(path.as_ref())?)?))
}
fn leak (x: impl AsRef<str>) -> &'static str {
Box::leak(x.as_ref().into())
}
fn unquote (x: &str) -> &str {
let mut chars = x.chars();
chars.next();
//chars.next_back();
chars.as_str()
}

View file

@ -15,6 +15,7 @@
#![feature(trait_alias)]
#![feature(type_changing_struct_update)]
#![feature(let_chains)]
#![feature(closure_lifetime_binder)]
/// Standard result type.
pub type Usually<T> = std::result::Result<T, Box<dyn std::error::Error>>;
/// Standard optional result type.

View file

@ -100,7 +100,16 @@ impl Cli {
for (index, connect) in midi_tos.iter().enumerate() {
let port = JackMidiOut::new(jack, &format!("{index}/M"), &[connect.clone()])?;
midi_outs.push(port);
}
};
let config_path = match mode {
LaunchMode::Clock => "config/config_transport.edn",
LaunchMode::Sequencer => "config/config_sequencer.edn",
LaunchMode::Groovebox => "config/config_groovebox.edn",
LaunchMode::Arranger { .. } => "config/config_arranger.edn",
LaunchMode::Sampler => "config/config_sampler.edn",
_ => todo!("{mode:?}"),
};
let config = Configuration::new(&config_path, false)?;
let mut app = Tek {
jack: jack.clone(),
color: ItemTheme::random(),
@ -139,14 +148,7 @@ impl Cli {
},
scenes,
selected: Selection::TrackClip { track: 0, scene: 0 },
config: Configuration::from_file(match mode {
LaunchMode::Clock => &"config/config_transport.edn",
LaunchMode::Sequencer => &"config/config_sequencer.edn",
LaunchMode::Groovebox => &"config/config_groovebox.edn",
LaunchMode::Arranger { .. } => &"config/config_arranger.edn",
LaunchMode::Sampler => &"config/config_sampler.edn",
_ => todo!("{mode:?}"),
}, false)?,
config,
..Default::default()
};
if let &LaunchMode::Arranger { scenes, tracks, track_width, .. } = mode {