mirror of
https://codeberg.org/unspeaker/tek.git
synced 2025-12-06 11:46:41 +01:00
wip: trait EdnCommand
This commit is contained in:
parent
8850fbf2f8
commit
aad7aa6c5e
7 changed files with 193 additions and 165 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -1446,6 +1446,9 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tek_input"
|
name = "tek_input"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
dependencies = [
|
||||||
|
"tek_edn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tek_jack"
|
name = "tek_jack"
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@ edition = "2021"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
tek_edn = { path = "../edn" }
|
||||||
|
|
|
||||||
30
input/src/edn_cmd.rs
Normal file
30
input/src/edn_cmd.rs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
use crate::*;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
pub trait EdnControlData<E: Input> {}
|
||||||
|
|
||||||
|
/// Renders from EDN source and context.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub enum EdnControls<E: Input, T: EdnControlData<E>> {
|
||||||
|
#[default]
|
||||||
|
Inert,
|
||||||
|
_Unused(PhantomData<E>),
|
||||||
|
Ok(T, EdnItem<String>),
|
||||||
|
Err(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Input, T: EdnControlData<E>> EdnControls<E, T> {
|
||||||
|
pub fn from_source (state: T, source: &str) -> Self {
|
||||||
|
match EdnItem::read_one(&source) {
|
||||||
|
Ok((layout, _)) => Self::Ok(state, layout),
|
||||||
|
Err(error) => Self::Err(format!("{error}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn from_items (state: T, items: &[EdnItem<&str>]) -> Self {
|
||||||
|
Self::Ok(state, EdnItem::Exp(items.iter().map(|i|(*i).clone()).collect()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait EdnCommand<C>: Command<C> {
|
||||||
|
fn from_edn <'a> (state: &C, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self;
|
||||||
|
}
|
||||||
|
|
@ -5,7 +5,9 @@ mod input; pub use self::input::*;
|
||||||
mod handle; pub use self::handle::*;
|
mod handle; pub use self::handle::*;
|
||||||
mod command; pub use self::command::*;
|
mod command; pub use self::command::*;
|
||||||
mod event_map; pub use self::event_map::*;
|
mod event_map; pub use self::event_map::*;
|
||||||
|
mod edn_cmd; pub use self::edn_cmd::*;
|
||||||
|
|
||||||
|
pub(crate) use ::tek_edn::EdnItem;
|
||||||
/// Standard error trait.
|
/// Standard error trait.
|
||||||
pub(crate) use std::error::Error;
|
pub(crate) use std::error::Error;
|
||||||
/// Standard result type.
|
/// Standard result type.
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,30 @@ audio!(|self: App, client, scope|{
|
||||||
Control::Continue
|
Control::Continue
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Hosts the JACK callback for a collection of tracks
|
||||||
|
pub struct TracksAudio<'a>(
|
||||||
|
// Track collection
|
||||||
|
pub &'a mut [ArrangerTrack],
|
||||||
|
/// Note buffer
|
||||||
|
pub &'a mut Vec<u8>,
|
||||||
|
/// Note chunk buffer
|
||||||
|
pub &'a mut Vec<Vec<Vec<u8>>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
impl Audio for TracksAudio<'_> {
|
||||||
|
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
||||||
|
let model = &mut self.0;
|
||||||
|
let note_buffer = &mut self.1;
|
||||||
|
let output_buffer = &mut self.2;
|
||||||
|
for track in model.iter_mut() {
|
||||||
|
if PlayerAudio(track.player_mut(), note_buffer, output_buffer).process(client, scope) == Control::Quit {
|
||||||
|
return Control::Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Control::Continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//audio!(|self: Sequencer, client, scope|{
|
//audio!(|self: Sequencer, client, scope|{
|
||||||
//// Start profiling cycle
|
//// Start profiling cycle
|
||||||
//let t0 = self.perf.get_t0();
|
//let t0 = self.perf.get_t0();
|
||||||
|
|
@ -206,27 +230,3 @@ audio!(|self: App, client, scope|{
|
||||||
//self.perf.update(t0, scope);
|
//self.perf.update(t0, scope);
|
||||||
//return Control::Continue
|
//return Control::Continue
|
||||||
//});
|
//});
|
||||||
|
|
||||||
/// Hosts the JACK callback for a collection of tracks
|
|
||||||
pub struct TracksAudio<'a>(
|
|
||||||
// Track collection
|
|
||||||
pub &'a mut [ArrangerTrack],
|
|
||||||
/// Note buffer
|
|
||||||
pub &'a mut Vec<u8>,
|
|
||||||
/// Note chunk buffer
|
|
||||||
pub &'a mut Vec<Vec<Vec<u8>>>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Audio for TracksAudio<'_> {
|
|
||||||
#[inline] fn process (&mut self, client: &Client, scope: &ProcessScope) -> Control {
|
|
||||||
let model = &mut self.0;
|
|
||||||
let note_buffer = &mut self.1;
|
|
||||||
let output_buffer = &mut self.2;
|
|
||||||
for track in model.iter_mut() {
|
|
||||||
if PlayerAudio(track.player_mut(), note_buffer, output_buffer).process(client, scope) == Control::Quit {
|
|
||||||
return Control::Quit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Control::Continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -31,126 +31,43 @@ handle!(TuiIn: |self: App, input| Ok(None));
|
||||||
Track(TrackCommand),
|
Track(TrackCommand),
|
||||||
Zoom(usize),
|
Zoom(usize),
|
||||||
}
|
}
|
||||||
impl AppCommand {
|
impl EdnCommand<App> for AppCommand {
|
||||||
pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||||
match (head, tail) {
|
match (head, tail) {
|
||||||
(Key("clear"), [ ]) => Self::Clear,
|
(Key("clear"), [ ]) =>
|
||||||
(Key("clip"), [a, b @ ..]) => Self::Clip(ClipCommand::from_edn(&a.to_ref(), b)),
|
Self::Clear,
|
||||||
(Key("clock"), [a, b @ ..]) => Self::Clock(ClockCommand::from_edn(&a.to_ref(), b)),
|
(Key("clip"), [a, b @ ..]) =>
|
||||||
(Key("color"), [a ]) => Self::Color(ItemPalette::default()),
|
Self::Clip(ClipCommand::from_edn(state, &a.to_ref(), b)),
|
||||||
(Key("compact"), [a ]) => Self::Compact(true),
|
(Key("clock"), [a, b @ ..]) =>
|
||||||
(Key("editor"), [a, b @ ..]) => Self::Editor(MidiEditCommand::from_edn(&a.to_ref(), b)),
|
Self::Clock(ClockCommand::from_edn(state, &a.to_ref(), b)),
|
||||||
(Key("enqueue"), [a ]) => Self::Enqueue(None),
|
(Key("color"), [a ]) =>
|
||||||
(Key("history"), [a ]) => Self::History(0),
|
Self::Color(ItemPalette::default()),
|
||||||
(Key("pool"), [a, b @ ..]) => Self::Pool(PoolCommand::from_edn(&a.to_ref(), b)),
|
(Key("compact"), [a ]) =>
|
||||||
(Key("sampler"), [a, b @ ..]) => Self::Sampler(SamplerCommand::from_edn(&a.to_ref(), b)),
|
Self::Compact(true),
|
||||||
(Key("scene"), [a, b @ ..]) => Self::Scene(SceneCommand::from_edn(&a.to_ref(), b)),
|
(Key("editor"), [a, b @ ..]) =>
|
||||||
(Key("select"), [a ]) => Self::Select(ArrangerSelection::Mix),
|
Self::Editor(MidiEditCommand::from_edn(state, &a.to_ref(), b)),
|
||||||
(Key("stop-all"), [ ]) => Self::StopAll,
|
(Key("enqueue"), [a ]) =>
|
||||||
(Key("track"), [a, b @ ..]) => Self::Track(TrackCommand::from_edn(&a.to_ref(), b)),
|
Self::Enqueue(None),
|
||||||
(Key("zoom"), [a ]) => Self::Zoom(0),
|
(Key("history"), [a ]) =>
|
||||||
|
Self::History(0),
|
||||||
|
(Key("pool"), [a, b @ ..]) =>
|
||||||
|
Self::Pool(PoolCommand::from_edn(state, &a.to_ref(), b)),
|
||||||
|
(Key("sampler"), [a, b @ ..]) =>
|
||||||
|
Self::Sampler(SamplerCommand::from_edn(state, &a.to_ref(), b)),
|
||||||
|
(Key("scene"), [a, b @ ..]) =>
|
||||||
|
Self::Scene(SceneCommand::from_edn(state, &a.to_ref(), b)),
|
||||||
|
(Key("select"), [a ]) =>
|
||||||
|
Self::Select(ArrangerSelection::Mix),
|
||||||
|
(Key("stop-all"), [ ]) =>
|
||||||
|
Self::StopAll,
|
||||||
|
(Key("track"), [a, b @ ..]) =>
|
||||||
|
Self::Track(TrackCommand::from_edn(state, &a.to_ref(), b)),
|
||||||
|
(Key("zoom"), [a ]) =>
|
||||||
|
Self::Zoom(0),
|
||||||
_ => panic!(),
|
_ => panic!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Clone, Debug)] pub enum ClipCommand {
|
|
||||||
Get(usize, usize),
|
|
||||||
Put(usize, usize, Option<Arc<RwLock<MidiClip>>>),
|
|
||||||
Enqueue(usize, usize),
|
|
||||||
Edit(Option<Arc<RwLock<MidiClip>>>),
|
|
||||||
SetLoop(usize, usize, bool),
|
|
||||||
SetColor(usize, usize, ItemPalette),
|
|
||||||
}
|
|
||||||
impl ClipCommand {
|
|
||||||
pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
|
||||||
match (head, tail) {
|
|
||||||
(Key("get"), [a, b ]) => Self::Get(0, 0),
|
|
||||||
(Key("put"), [a, b, c ]) => Self::Put(0, 0, None),
|
|
||||||
(Key("enqueue"), [a, b ]) => Self::Enqueue(0, 0),
|
|
||||||
(Key("edit"), [a ]) => Self::Edit(None),
|
|
||||||
(Key("loop"), [a, b, c ]) => Self::SetLoop(0, 0, true),
|
|
||||||
(Key("color"), [a, b, c ]) => Self::SetColor(0, 0, ItemPalette::random()),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)] pub enum SceneCommand {
|
|
||||||
Add,
|
|
||||||
Del(usize),
|
|
||||||
Swap(usize, usize),
|
|
||||||
SetSize(usize),
|
|
||||||
SetZoom(usize),
|
|
||||||
SetColor(usize, ItemPalette),
|
|
||||||
Enqueue(usize),
|
|
||||||
}
|
|
||||||
impl SceneCommand {
|
|
||||||
pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
|
||||||
match (head, tail) {
|
|
||||||
(Key("add"), [ ]) => Self::Add,
|
|
||||||
(Key("del"), [a ]) => Self::Del(0),
|
|
||||||
(Key("swap"), [a, b ]) => Self::Swap(0, 0),
|
|
||||||
(Key("size"), [a ]) => Self::SetSize(0),
|
|
||||||
(Key("zoom"), [a, ]) => Self::SetZoom(0),
|
|
||||||
(Key("color"), [a, b, ]) => Self::SetColor(0, ItemPalette::random()),
|
|
||||||
(Key("enqueue"), [a, ]) => Self::Enqueue(0),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)] pub enum TrackCommand {
|
|
||||||
Add,
|
|
||||||
Del(usize),
|
|
||||||
Stop(usize),
|
|
||||||
Swap(usize, usize),
|
|
||||||
SetSize(usize),
|
|
||||||
SetZoom(usize),
|
|
||||||
SetColor(usize, ItemPalette),
|
|
||||||
}
|
|
||||||
impl TrackCommand {
|
|
||||||
pub fn from_edn <'a> (head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
|
||||||
match (head, tail) {
|
|
||||||
(Key("add"), [ ]) => Self::Add,
|
|
||||||
(Key("del"), [a ]) => Self::Del(0),
|
|
||||||
(Key("stop"), [a ]) => Self::Stop(0),
|
|
||||||
(Key("swap"), [a, b ]) => Self::Swap(0, 0),
|
|
||||||
(Key("size"), [a ]) => Self::SetSize(0),
|
|
||||||
(Key("zoom"), [a, ]) => Self::SetZoom(0),
|
|
||||||
(Key("color"), [a, b, ]) => Self::SetColor(0, ItemPalette::random()),
|
|
||||||
_ => panic!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)] pub enum SequencerCommand {
|
|
||||||
Compact(bool),
|
|
||||||
History(isize),
|
|
||||||
Clock(ClockCommand),
|
|
||||||
Pool(PoolCommand),
|
|
||||||
Editor(MidiEditCommand),
|
|
||||||
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)] pub enum GrooveboxCommand {
|
|
||||||
Compact(bool),
|
|
||||||
History(isize),
|
|
||||||
Clock(ClockCommand),
|
|
||||||
Pool(PoolCommand),
|
|
||||||
Editor(MidiEditCommand),
|
|
||||||
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
|
||||||
Sampler(SamplerCommand),
|
|
||||||
}
|
|
||||||
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
|
||||||
History(isize),
|
|
||||||
Color(ItemPalette),
|
|
||||||
Clock(ClockCommand),
|
|
||||||
Scene(SceneCommand),
|
|
||||||
Track(TrackCommand),
|
|
||||||
Clip(ClipCommand),
|
|
||||||
Select(ArrangerSelection),
|
|
||||||
Zoom(usize),
|
|
||||||
Pool(PoolCommand),
|
|
||||||
Editor(MidiEditCommand),
|
|
||||||
StopAll,
|
|
||||||
Clear,
|
|
||||||
}
|
|
||||||
command!(|self: AppCommand, state: App|match self {
|
command!(|self: AppCommand, state: App|match self {
|
||||||
Self::Clear => { todo!() },
|
Self::Clear => { todo!() },
|
||||||
Self::Zoom(_) => { todo!(); },
|
Self::Zoom(_) => { todo!(); },
|
||||||
|
|
@ -238,6 +155,107 @@ command!(|self: AppCommand, state: App|match self {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
#[derive(Clone, Debug)] pub enum ClipCommand {
|
||||||
|
Get(usize, usize),
|
||||||
|
Put(usize, usize, Option<Arc<RwLock<MidiClip>>>),
|
||||||
|
Enqueue(usize, usize),
|
||||||
|
Edit(Option<Arc<RwLock<MidiClip>>>),
|
||||||
|
SetLoop(usize, usize, bool),
|
||||||
|
SetColor(usize, usize, ItemPalette),
|
||||||
|
}
|
||||||
|
impl EdnCommand<App> for ClipCommand {
|
||||||
|
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||||
|
match (head, tail) {
|
||||||
|
(Key("get"), [a, b ]) => Self::Get(0, 0),
|
||||||
|
(Key("put"), [a, b, c ]) => Self::Put(0, 0, None),
|
||||||
|
(Key("enqueue"), [a, b ]) => Self::Enqueue(0, 0),
|
||||||
|
(Key("edit"), [a ]) => Self::Edit(None),
|
||||||
|
(Key("loop"), [a, b, c ]) => Self::SetLoop(0, 0, true),
|
||||||
|
(Key("color"), [a, b, c ]) => Self::SetColor(0, 0, ItemPalette::random()),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command!(|self: ClipCommand, state: App|match self { _ => todo!("clip command") });
|
||||||
|
#[derive(Clone, Debug)] pub enum SceneCommand {
|
||||||
|
Add,
|
||||||
|
Del(usize),
|
||||||
|
Swap(usize, usize),
|
||||||
|
SetSize(usize),
|
||||||
|
SetZoom(usize),
|
||||||
|
SetColor(usize, ItemPalette),
|
||||||
|
Enqueue(usize),
|
||||||
|
}
|
||||||
|
impl EdnCommand<App> for SceneCommand {
|
||||||
|
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||||
|
match (head, tail) {
|
||||||
|
(Key("add"), [ ]) => Self::Add,
|
||||||
|
(Key("del"), [a ]) => Self::Del(0),
|
||||||
|
(Key("swap"), [a, b ]) => Self::Swap(0, 0),
|
||||||
|
(Key("size"), [a ]) => Self::SetSize(0),
|
||||||
|
(Key("zoom"), [a, ]) => Self::SetZoom(0),
|
||||||
|
(Key("color"), [a, b, ]) => Self::SetColor(0, ItemPalette::random()),
|
||||||
|
(Key("enqueue"), [a, ]) => Self::Enqueue(0),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command!(|self: SceneCommand, state: App|match self { _ => todo!("scene command") });
|
||||||
|
#[derive(Clone, Debug)] pub enum TrackCommand {
|
||||||
|
Add,
|
||||||
|
Del(usize),
|
||||||
|
Stop(usize),
|
||||||
|
Swap(usize, usize),
|
||||||
|
SetSize(usize),
|
||||||
|
SetZoom(usize),
|
||||||
|
SetColor(usize, ItemPalette),
|
||||||
|
}
|
||||||
|
impl EdnCommand<App> for TrackCommand {
|
||||||
|
fn from_edn <'a> (state: &App, head: &EdnItem<&str>, tail: &'a [EdnItem<String>]) -> Self {
|
||||||
|
match (head, tail) {
|
||||||
|
(Key("add"), [ ]) => Self::Add,
|
||||||
|
(Key("del"), [a ]) => Self::Del(0),
|
||||||
|
(Key("stop"), [a ]) => Self::Stop(0),
|
||||||
|
(Key("swap"), [a, b ]) => Self::Swap(0, 0),
|
||||||
|
(Key("size"), [a ]) => Self::SetSize(0),
|
||||||
|
(Key("zoom"), [a, ]) => Self::SetZoom(0),
|
||||||
|
(Key("color"), [a, b, ]) => Self::SetColor(0, ItemPalette::random()),
|
||||||
|
_ => panic!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
command!(|self: TrackCommand, state: App|match self { _ => todo!("track command") });
|
||||||
|
#[derive(Clone, Debug)] pub enum SequencerCommand {
|
||||||
|
Compact(bool),
|
||||||
|
History(isize),
|
||||||
|
Clock(ClockCommand),
|
||||||
|
Pool(PoolCommand),
|
||||||
|
Editor(MidiEditCommand),
|
||||||
|
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)] pub enum GrooveboxCommand {
|
||||||
|
Compact(bool),
|
||||||
|
History(isize),
|
||||||
|
Clock(ClockCommand),
|
||||||
|
Pool(PoolCommand),
|
||||||
|
Editor(MidiEditCommand),
|
||||||
|
Enqueue(Option<Arc<RwLock<MidiClip>>>),
|
||||||
|
Sampler(SamplerCommand),
|
||||||
|
}
|
||||||
|
#[derive(Clone, Debug)] pub enum ArrangerCommand {
|
||||||
|
History(isize),
|
||||||
|
Color(ItemPalette),
|
||||||
|
Clock(ClockCommand),
|
||||||
|
Scene(SceneCommand),
|
||||||
|
Track(TrackCommand),
|
||||||
|
Clip(ClipCommand),
|
||||||
|
Select(ArrangerSelection),
|
||||||
|
Zoom(usize),
|
||||||
|
Pool(PoolCommand),
|
||||||
|
Editor(MidiEditCommand),
|
||||||
|
StopAll,
|
||||||
|
Clear,
|
||||||
|
}
|
||||||
|
|
||||||
//command!(|self: SequencerCommand, state: Sequencer|match self {
|
//command!(|self: SequencerCommand, state: Sequencer|match self {
|
||||||
//Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?,
|
//Self::Clock(cmd) => cmd.delegate(state, Self::Clock)?,
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
use crate::*;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
pub trait EdnControlData<E: Input> {}
|
|
||||||
|
|
||||||
/// Renders from EDN source and context.
|
|
||||||
#[derive(Default)]
|
|
||||||
pub enum EdnControls<E: Input, T: EdnControlData<E>> {
|
|
||||||
#[default]
|
|
||||||
Inert,
|
|
||||||
_Unused(PhantomData<E>),
|
|
||||||
Ok(T, EdnItem<String>),
|
|
||||||
Err(String)
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: Input, T: EdnControlData<E>> EdnControls<E, T> {
|
|
||||||
pub fn from_source (state: T, source: &str) -> Self {
|
|
||||||
match EdnItem::read_one(&source) {
|
|
||||||
Ok((layout, _)) => Self::Ok(state, layout),
|
|
||||||
Err(error) => Self::Err(format!("{error}"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_items (state: T, items: &[EdnItem<&str>]) -> Self {
|
|
||||||
Self::Ok(state, EdnItem::Exp(items.iter().map(|i|(*i).clone()).collect()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue