diff --git a/musicdb-client/test.mers b/musicdb-client/test.mers new file mode 100644 index 0000000..b38c536 --- /dev/null +++ b/musicdb-client/test.mers @@ -0,0 +1,5 @@ +().all_songs.for_each({id: id, title: title, album: album, artist: artist, cover: cover} -> { + 5.sleep + ("Adding '", title, "' to queue.").concat.eprintln + ().as_list.queue_add_song(id) +}) diff --git a/musicdb-mers/Cargo.toml b/musicdb-mers/Cargo.toml new file mode 100644 index 0000000..5b12227 --- /dev/null +++ b/musicdb-mers/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "musicdb-mers" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +musicdb-lib = { version = "0.1.0", path = "../musicdb-lib" } +mers_lib = "0.3.4" diff --git a/musicdb-mers/src/lib.rs b/musicdb-mers/src/lib.rs new file mode 100644 index 0000000..b828d49 --- /dev/null +++ b/musicdb-mers/src/lib.rs @@ -0,0 +1,680 @@ +use std::{ + fmt::{Debug, Display}, + sync::{Arc, Mutex, RwLock}, +}; + +use mers_lib::{ + data::{self, Data, MersData, MersType, Type}, + info::Info, + prelude_extend_config::Config, +}; +use musicdb_lib::{ + data::{ + album::Album, + artist::Artist, + database::Database, + queue::{Queue, QueueContent}, + song::Song, + }, + server::Command, +}; + +pub fn add( + cfg: Config, + db: &Arc>, + cmd: &Arc, +) -> Config { + macro_rules! func { + ($out:expr, $run:expr) => { + Data::new(data::function::Function { + info: Arc::new(Info::neverused()), + info_check: Arc::new(Mutex::new(Info::neverused())), + out: Arc::new($out), + run: Arc::new($run), + inner_statements: None, + }) + }; + } + cfg.with_list() + .add_type(MusicDbIdT.to_string(), Ok(Arc::new(MusicDbIdT))) + .add_var( + "queue_get_current_song".to_owned(), + func!( + |a, _| { + if a.is_included_in(&Type::empty_tuple()) { + Ok(Type::newm(vec![ + Arc::new(MusicDbIdT), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `()`.").into()) + } + }, + { + let db = Arc::clone(db); + move |_, _| match db.lock().unwrap().queue.get_current_song() { + Some(id) => Data::new(MusicDbId(*id)), + None => Data::empty_tuple(), + } + } + ), + ) + .add_var( + "queue_get_next_song".to_owned(), + func!( + |a, _| { + if a.is_included_in(&Type::empty_tuple()) { + Ok(Type::newm(vec![ + Arc::new(MusicDbIdT), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `()`.").into()) + } + }, + { + let db = Arc::clone(db); + move |_, _| match db.lock().unwrap().queue.get_next_song() { + Some(id) => Data::new(MusicDbId(*id)), + None => Data::empty_tuple(), + } + } + ), + ) + .add_var( + "queue_get_elem".to_owned(), + func!( + |a, _| { + if a.is_included_in(&mers_lib::program::configs::with_list::ListT(Type::new( + data::int::IntT, + ))) { + Ok(gen_queue_elem_type()) + } else { + Err(format!("Function argument must be `List`.").into()) + } + }, + { + let db = Arc::clone(db); + move |a, _| { + let a = int_list_to_usize_vec(&a); + if let Some(elem) = db.lock().unwrap().queue.get_item_at_index(&a, 0) { + gen_queue_elem(elem) + } else { + Data::empty_tuple() + } + } + } + ), + ) + .add_var( + "queue_add_song".to_owned(), + func!( + |a, _| { + if a.is_included_in(&data::tuple::TupleT(vec![ + Type::new(mers_lib::program::configs::with_list::ListT(Type::new( + data::int::IntT, + ))), + Type::new(MusicDbIdT), + ])) { + Ok(Type::empty_tuple()) + } else { + Err(format!("Function argument must be `(List, MusicDbId)`.").into()) + } + }, + { + let cmd = cmd.clone(); + move |a, _| { + let a = a.get(); + let a = &a.as_any().downcast_ref::().unwrap().0; + let path = int_list_to_usize_vec(&a[0]); + let song_id = a[1].get().as_any().downcast_ref::().unwrap().0; + cmd(Command::QueueAdd( + path, + vec![QueueContent::Song(song_id).into()], + )); + Data::empty_tuple() + } + } + ), + ) + .add_var( + "all_songs".to_owned(), + func!( + |a, _| { + if a.is_zero_tuple() { + Ok(Type::new(mers_lib::program::configs::with_list::ListT( + gen_song_type(), + ))) + } else { + Err(format!("Function argument must be `()`.").into()) + } + }, + { + let db = Arc::clone(db); + move |_, _| { + Data::new(mers_lib::program::configs::with_list::List( + db.lock() + .unwrap() + .songs() + .values() + .map(|s| Arc::new(RwLock::new(gen_song(s)))) + .collect(), + )) + } + } + ), + ) + .add_var( + "get_song".to_owned(), + func!( + |a, _| { + if a.is_included_in(&MusicDbIdT) { + Ok(Type::newm(vec![ + Arc::new(gen_song_type()), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `MusicDbId`.").into()) + } + }, + { + let db = Arc::clone(db); + move |a, _| { + let id = a.get().as_any().downcast_ref::().unwrap().0; + match db.lock().unwrap().get_song(&id) { + Some(song) => gen_song(song), + None => Data::empty_tuple(), + } + } + } + ), + ) + .add_var( + "get_album".to_owned(), + func!( + |a, _| { + if a.is_included_in(&MusicDbIdT) { + Ok(Type::newm(vec![ + Arc::new(gen_album_type()), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `MusicDbId`.").into()) + } + }, + { + let db = Arc::clone(db); + move |a, _| { + let id = a.get().as_any().downcast_ref::().unwrap().0; + match db.lock().unwrap().albums().get(&id) { + Some(album) => gen_album(album), + None => Data::empty_tuple(), + } + } + } + ), + ) + .add_var( + "get_artist".to_owned(), + func!( + |a, _| { + if a.is_included_in(&MusicDbIdT) { + Ok(Type::newm(vec![ + Arc::new(gen_artist_type()), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `MusicDbId`.").into()) + } + }, + { + let db = Arc::clone(db); + move |a, _| { + let id = a.get().as_any().downcast_ref::().unwrap().0; + match db.lock().unwrap().artists().get(&id) { + Some(artist) => gen_artist(artist), + None => Data::empty_tuple(), + } + } + } + ), + ) + .add_var( + "get_song_tags".to_owned(), + func!( + |a, _| { + if a.is_included_in(&MusicDbIdT) { + Ok(Type::newm(vec![ + Arc::new(mers_lib::program::configs::with_list::ListT(Type::new( + data::string::StringT, + ))), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `MusicDbId`.").into()) + } + }, + { + let db = Arc::clone(db); + move |a, _| { + let id = a.get().as_any().downcast_ref::().unwrap().0; + match db.lock().unwrap().get_song(&id) { + Some(song) => Data::new(mers_lib::program::configs::with_list::List( + song.general + .tags + .iter() + .map(|t| { + Arc::new(RwLock::new(Data::new(data::string::String( + t.clone(), + )))) + }) + .collect(), + )), + None => Data::empty_tuple(), + } + } + } + ), + ) + .add_var( + "get_album_tags".to_owned(), + func!( + |a, _| { + if a.is_included_in(&MusicDbIdT) { + Ok(Type::newm(vec![ + Arc::new(mers_lib::program::configs::with_list::ListT(Type::new( + data::string::StringT, + ))), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `MusicDbId`.").into()) + } + }, + { + let db = Arc::clone(db); + move |a, _| { + let id = a.get().as_any().downcast_ref::().unwrap().0; + match db.lock().unwrap().albums().get(&id) { + Some(album) => Data::new(mers_lib::program::configs::with_list::List( + album + .general + .tags + .iter() + .map(|t| { + Arc::new(RwLock::new(Data::new(data::string::String( + t.clone(), + )))) + }) + .collect(), + )), + None => Data::empty_tuple(), + } + } + } + ), + ) + .add_var( + "get_artist_tags".to_owned(), + func!( + |a, _| { + if a.is_included_in(&MusicDbIdT) { + Ok(Type::newm(vec![ + Arc::new(mers_lib::program::configs::with_list::ListT(Type::new( + data::string::StringT, + ))), + Arc::new(data::tuple::TupleT(vec![])), + ])) + } else { + Err(format!("Function argument must be `MusicDbId`.").into()) + } + }, + { + let db = Arc::clone(db); + move |a, _| { + let id = a.get().as_any().downcast_ref::().unwrap().0; + match db.lock().unwrap().artists().get(&id) { + Some(artist) => Data::new(mers_lib::program::configs::with_list::List( + artist + .general + .tags + .iter() + .map(|t| { + Arc::new(RwLock::new(Data::new(data::string::String( + t.clone(), + )))) + }) + .collect(), + )), + None => Data::empty_tuple(), + } + } + } + ), + ) +} + +fn gen_song_type() -> Type { + Type::new(data::object::ObjectT(vec![ + ("id".to_owned(), Type::new(MusicDbIdT)), + ("title".to_owned(), Type::new(data::string::StringT)), + ( + "album".to_owned(), + Type::newm(vec![ + Arc::new(MusicDbIdT), + Arc::new(data::tuple::TupleT(vec![])), + ]), + ), + ("artist".to_owned(), Type::new(MusicDbIdT)), + ( + "cover".to_owned(), + Type::newm(vec![ + Arc::new(MusicDbIdT), + Arc::new(data::tuple::TupleT(vec![])), + ]), + ), + ])) +} +fn gen_song(song: &Song) -> Data { + Data::new(data::object::Object(vec![ + ("id".to_owned(), Data::new(MusicDbId(song.id))), + ( + "title".to_owned(), + Data::new(data::string::String(song.title.clone())), + ), + ( + "album".to_owned(), + if let Some(album) = song.album { + Data::new(MusicDbId(album)) + } else { + Data::empty_tuple() + }, + ), + ("artist".to_owned(), Data::new(MusicDbId(song.artist))), + ( + "cover".to_owned(), + if let Some(cover) = song.cover { + Data::new(MusicDbId(cover)) + } else { + Data::empty_tuple() + }, + ), + ])) +} +fn gen_album_type() -> Type { + Type::new(data::object::ObjectT(vec![ + ("id".to_owned(), Type::new(MusicDbIdT)), + ("name".to_owned(), Type::new(data::string::StringT)), + ("artist".to_owned(), Type::new(MusicDbIdT)), + ( + "cover".to_owned(), + Type::newm(vec![ + Arc::new(MusicDbIdT), + Arc::new(data::tuple::TupleT(vec![])), + ]), + ), + ( + "songs".to_owned(), + Type::new(mers_lib::program::configs::with_list::ListT(Type::new( + MusicDbIdT, + ))), + ), + ])) +} +fn gen_album(album: &Album) -> Data { + Data::new(data::object::Object(vec![ + ("id".to_owned(), Data::new(MusicDbId(album.id))), + ( + "name".to_owned(), + Data::new(data::string::String(album.name.clone())), + ), + ("artist".to_owned(), Data::new(MusicDbId(album.artist))), + ( + "cover".to_owned(), + if let Some(cover) = album.cover { + Data::new(MusicDbId(cover)) + } else { + Data::empty_tuple() + }, + ), + ( + "songs".to_owned(), + Data::new(mers_lib::program::configs::with_list::List( + album + .songs + .iter() + .map(|id| Arc::new(RwLock::new(Data::new(MusicDbId(*id))))) + .collect(), + )), + ), + ])) +} +fn gen_artist_type() -> Type { + Type::new(data::object::ObjectT(vec![ + ("id".to_owned(), Type::new(MusicDbIdT)), + ("name".to_owned(), Type::new(data::string::StringT)), + ( + "cover".to_owned(), + Type::newm(vec![ + Arc::new(MusicDbIdT), + Arc::new(data::tuple::TupleT(vec![])), + ]), + ), + ( + "albums".to_owned(), + Type::new(mers_lib::program::configs::with_list::ListT(Type::new( + MusicDbIdT, + ))), + ), + ( + "singles".to_owned(), + Type::new(mers_lib::program::configs::with_list::ListT(Type::new( + MusicDbIdT, + ))), + ), + ])) +} +fn gen_artist(artist: &Artist) -> Data { + Data::new(data::object::Object(vec![ + ("id".to_owned(), Data::new(MusicDbId(artist.id))), + ( + "name".to_owned(), + Data::new(data::string::String(artist.name.clone())), + ), + ( + "cover".to_owned(), + if let Some(cover) = artist.cover { + Data::new(MusicDbId(cover)) + } else { + Data::empty_tuple() + }, + ), + ( + "albums".to_owned(), + Data::new(mers_lib::program::configs::with_list::List( + artist + .albums + .iter() + .map(|id| Arc::new(RwLock::new(Data::new(MusicDbId(*id))))) + .collect(), + )), + ), + ( + "singles".to_owned(), + Data::new(mers_lib::program::configs::with_list::List( + artist + .singles + .iter() + .map(|id| Arc::new(RwLock::new(Data::new(MusicDbId(*id))))) + .collect(), + )), + ), + ])) +} + +fn gen_queue_elem_type() -> Type { + Type::newm(vec![ + Arc::new(data::tuple::TupleT(vec![])), + Arc::new(data::object::ObjectT(vec![ + ("enabled".to_owned(), Type::new(data::bool::BoolT)), + ("song".to_owned(), Type::new(MusicDbIdT)), + ])), + Arc::new(data::object::ObjectT(vec![ + ("enabled".to_owned(), Type::new(data::bool::BoolT)), + ( + "loop".to_owned(), + Type::new(data::object::ObjectT(vec![ + ("total".to_owned(), Type::new(data::int::IntT)), + ("done".to_owned(), Type::new(data::int::IntT)), + ])), + ), + ])), + Arc::new(data::object::ObjectT(vec![ + ("enabled".to_owned(), Type::new(data::bool::BoolT)), + ("random".to_owned(), Type::empty_tuple()), + ])), + Arc::new(data::object::ObjectT(vec![ + ("enabled".to_owned(), Type::new(data::bool::BoolT)), + ( + "folder".to_owned(), + Type::new(data::object::ObjectT(vec![ + ("index".to_owned(), Type::new(data::int::IntT)), + ("length".to_owned(), Type::new(data::int::IntT)), + ("name".to_owned(), Type::new(data::string::StringT)), + ])), + ), + ])), + Arc::new(data::object::ObjectT(vec![ + ("enabled".to_owned(), Type::new(data::bool::BoolT)), + ("shuffle".to_owned(), Type::empty_tuple()), + ])), + ]) +} +fn gen_queue_elem(queue_elem: &Queue) -> Data { + Data::new(data::object::Object(vec![ + ( + "enabled".to_owned(), + Data::new(data::bool::Bool(queue_elem.enabled())), + ), + match queue_elem.content() { + QueueContent::Song(id) => ("song".to_owned(), Data::new(MusicDbId(*id))), + QueueContent::Loop(total, done, _inner) => ( + "loop".to_owned(), + Data::new(data::object::Object(vec![ + ("total".to_owned(), Data::new(data::int::Int(*total as _))), + ("done".to_owned(), Data::new(data::int::Int(*done as _))), + ])), + ), + QueueContent::Random(_) => ("random".to_owned(), Data::empty_tuple()), + QueueContent::Folder(index, inner, name) => ( + "folder".to_owned(), + Data::new(data::object::Object(vec![ + ("index".to_owned(), Data::new(data::int::Int(*index as _))), + ( + "length".to_owned(), + Data::new(data::int::Int(inner.len() as _)), + ), + ( + "name".to_owned(), + Data::new(data::string::String(name.clone())), + ), + ])), + ), + QueueContent::Shuffle { inner: _, state: _ } => { + ("shuffle".to_owned(), Data::empty_tuple()) + } + }, + ])) +} + +fn int_list_to_usize_vec(a: &Data) -> Vec { + a.get() + .as_any() + .downcast_ref::() + .unwrap() + .0 + .iter() + .map(|v| { + v.read() + .unwrap() + .get() + .as_any() + .downcast_ref::() + .unwrap() + .0 + .abs() as usize + }) + .collect() +} + +#[derive(Clone, Copy)] +pub struct MusicDbId(u64); + +#[derive(Clone, Copy)] +pub struct MusicDbIdT; + +impl MersData for MusicDbId { + fn as_type(&self) -> Type { + Type::new(MusicDbIdT) + } + fn is_eq(&self, other: &dyn MersData) -> bool { + if let Some(other) = other.as_any().downcast_ref::() { + self.0 == other.0 + } else { + false + } + } + fn clone(&self) -> Box { + Box::new(*self) + } + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn mut_any(&mut self) -> &mut dyn std::any::Any { + self + } + fn to_any(self) -> Box { + Box::new(self) + } +} +impl MersType for MusicDbIdT { + fn is_same_type_as(&self, other: &dyn MersType) -> bool { + other.as_any().is::() + } + fn is_included_in_single(&self, target: &dyn MersType) -> bool { + target.as_any().is::() + } + fn subtypes(&self, acc: &mut Type) { + acc.add(Arc::new(*self)) + } + fn as_any(&self) -> &dyn std::any::Any { + self + } + fn mut_any(&mut self) -> &mut dyn std::any::Any { + self + } + fn to_any(self) -> Box { + Box::new(self) + } +} + +impl Display for MusicDbId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} +impl Debug for MusicDbId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self}") + } +} +impl Display for MusicDbIdT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "MusicDbId") + } +} +impl Debug for MusicDbIdT { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{self}") + } +}