cargo fmt
This commit is contained in:
parent
1198f58862
commit
c3d605420f
47 changed files with 738 additions and 451 deletions
45
Cargo.lock
generated
45
Cargo.lock
generated
|
@ -692,9 +692,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88bffebc5d80432c9b140ee17875ff173a8ab62faad5b257da912bd2f6c1c0a1"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "fallible-iterator"
|
||||
|
@ -1131,17 +1131,6 @@ dependencies = [
|
|||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mpd"
|
||||
version = "0.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57a20784da57fa01bf7910a5da686d9f39ff37feaa774856b71f050e4331bf82"
|
||||
dependencies = [
|
||||
"bufstream",
|
||||
"rustc-serialize",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mpd"
|
||||
version = "0.1.0"
|
||||
|
@ -1178,7 +1167,7 @@ dependencies = [
|
|||
"criterion",
|
||||
"dirs",
|
||||
"m3u8-rs",
|
||||
"mpd 0.1.0",
|
||||
"mpd",
|
||||
"rand 0.8.5",
|
||||
"regex 1.9.1",
|
||||
"rusqlite",
|
||||
|
@ -1203,7 +1192,7 @@ version = "0.9.0"
|
|||
dependencies = [
|
||||
"base64",
|
||||
"m3u8-rs",
|
||||
"mpd 0.0.12",
|
||||
"mpd",
|
||||
"mpris-player",
|
||||
"muss-interpreter",
|
||||
"rodio",
|
||||
|
@ -1609,9 +1598,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "portable-atomic"
|
||||
version = "1.3.3"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "767eb9f07d4a5ebcb39bbf2d452058a93c011373abf6832e24194a1c3f004794"
|
||||
checksum = "d220334a184db82b31b83f5ff093e3315280fb2b6bbc032022b2304a509aab7a"
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
|
@ -1870,7 +1859,7 @@ dependencies = [
|
|||
"aho-corasick 1.0.2",
|
||||
"memchr 2.5.0",
|
||||
"regex-automata",
|
||||
"regex-syntax 0.7.3",
|
||||
"regex-syntax 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1881,7 +1870,7 @@ checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf"
|
|||
dependencies = [
|
||||
"aho-corasick 1.0.2",
|
||||
"memchr 2.5.0",
|
||||
"regex-syntax 0.7.3",
|
||||
"regex-syntax 0.7.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1892,9 +1881,9 @@ checksum = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.7.3"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ab07dc67230e4a4718e70fd5c20055a4334b121f1f9db8fe63ef39ce9b8c846"
|
||||
checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
|
@ -1965,12 +1954,6 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-serialize"
|
||||
version = "0.3.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.2.3"
|
||||
|
@ -2047,9 +2030,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
|||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.170"
|
||||
version = "1.0.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a56657f512baabca8f840542f9ca8152aecf182c473c26e46e58d6aab4f6e439"
|
||||
checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
@ -2066,9 +2049,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.170"
|
||||
version = "1.0.171"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77d477848e6b23adba0db397777d5aad864555bc17fd9c89abb3b8009788b7b8"
|
||||
checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -6,13 +6,14 @@ pub(crate) fn standard_vocab(vocabulary: &mut LanguageDictionary) {
|
|||
// filters
|
||||
.add_transform(crate::lang::vocabulary::filters::empty_filter())
|
||||
.add_transform(crate::lang::vocabulary::filters::range_filter())
|
||||
.add_transform( // accepts any .(.something)
|
||||
.add_transform(
|
||||
// accepts any .(.something)
|
||||
crate::lang::vocabulary::filters::field::FieldFilterBlockFactory::new()
|
||||
.push(crate::lang::vocabulary::filters::field::FieldFilterComparisonFactory)
|
||||
.push(crate::lang::vocabulary::filters::field::FieldFilterMaybeFactory)
|
||||
.push(crate::lang::vocabulary::filters::field::FieldLikeFilterFactory)
|
||||
.push(crate::lang::vocabulary::filters::field::FieldRegexFilterFactory)
|
||||
.to_statement_factory()
|
||||
.to_statement_factory(),
|
||||
)
|
||||
.add_transform(crate::lang::vocabulary::filters::unique_field_filter())
|
||||
.add_transform(crate::lang::vocabulary::filters::unique_filter())
|
||||
|
@ -48,7 +49,7 @@ pub(crate) fn standard_vocab(vocabulary: &mut LanguageDictionary) {
|
|||
.push(crate::lang::vocabulary::item_ops::BracketsItemOpFactory)
|
||||
.push(crate::lang::vocabulary::item_ops::FieldRetrieveItemOpFactory)
|
||||
.push(crate::lang::vocabulary::item_ops::ConstantItemOpFactory)
|
||||
.push(crate::lang::vocabulary::item_ops::VariableRetrieveItemOpFactory)
|
||||
.push(crate::lang::vocabulary::item_ops::VariableRetrieveItemOpFactory),
|
||||
)
|
||||
// functions and misc
|
||||
// functions don't enforce bracket coherence
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use super::SyntaxError;
|
||||
use super::{BoxedOpFactory, Op, BoxedTransformOpFactory};
|
||||
use super::{BoxedOpFactory, BoxedTransformOpFactory, Op};
|
||||
use crate::tokens::Token;
|
||||
|
||||
pub struct LanguageDictionary {
|
||||
|
@ -69,7 +69,7 @@ impl LanguageDictionary {
|
|||
for factory in &self.transform_vocabulary {
|
||||
if factory.is_transform_op(tokens) {
|
||||
op = factory.build_transform_op(tokens, self, op)?;
|
||||
return Ok((op, true))
|
||||
return Ok((op, true));
|
||||
}
|
||||
}
|
||||
Ok((op, false))
|
||||
|
|
|
@ -135,7 +135,7 @@ impl<P: FilterPredicate + 'static> Op for FilterStatement<P> {
|
|||
}
|
||||
}
|
||||
|
||||
impl <P: FilterPredicate + 'static> FilterStatement<P> {
|
||||
impl<P: FilterPredicate + 'static> FilterStatement<P> {
|
||||
fn next_item(&mut self) -> Option<IteratorItem> {
|
||||
match self.iterable.try_real() {
|
||||
Ok(real_op) => {
|
||||
|
@ -163,7 +163,9 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
|
|||
match next_item {
|
||||
Ok(item) => {
|
||||
//let ctx = self.context.as_mut().unwrap();
|
||||
let matches_result = self.predicate.matches(&item, self.context.as_mut().unwrap());
|
||||
let matches_result = self
|
||||
.predicate
|
||||
.matches(&item, self.context.as_mut().unwrap());
|
||||
let matches = match matches_result {
|
||||
Err(e) => {
|
||||
//maybe_result = Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))));
|
||||
|
@ -177,30 +179,41 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
|
|||
// make fake inner item
|
||||
//println!("Making fake inner variable `{}`", INNER_VARIABLE_NAME);
|
||||
let single_op = SingleItem::new_ok(item.clone());
|
||||
let preexisting_var = self.context.as_mut().unwrap()
|
||||
let preexisting_var = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.swap(INNER_VARIABLE_NAME, Some(Type::Op(Box::new(single_op))));
|
||||
let inner_real = match inner.try_real() {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Some(Err(e))
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
inner_real.enter(self.context.take().unwrap());
|
||||
match inner_real.next() {
|
||||
Some(item) => {
|
||||
self.context = Some(inner_real.escape());
|
||||
self.context.as_mut().unwrap().variables.swap(INNER_VARIABLE_NAME, preexisting_var);
|
||||
self.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.swap(INNER_VARIABLE_NAME, preexisting_var);
|
||||
return Some(item);
|
||||
}
|
||||
None => {
|
||||
self.context = Some(inner_real.escape()); // move ctx back to expected spot
|
||||
self.context.as_mut().unwrap().variables.swap(INNER_VARIABLE_NAME, preexisting_var);
|
||||
self.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.swap(INNER_VARIABLE_NAME, preexisting_var);
|
||||
}
|
||||
}
|
||||
}
|
||||
if matches {
|
||||
return Some(Ok(item));
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
}
|
||||
|
@ -208,7 +221,11 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
|
|||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iterable.try_real_ref().map(|x| x.size_hint()).ok().unwrap_or((0, None))
|
||||
self.iterable
|
||||
.try_real_ref()
|
||||
.map(|x| x.size_hint())
|
||||
.ok()
|
||||
.unwrap_or((0, None))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,7 +244,8 @@ impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> FilterStatemen
|
|||
}
|
||||
|
||||
impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> BoxedTransformOpFactory
|
||||
for FilterStatementFactory<P, F> {
|
||||
for FilterStatementFactory<P, F>
|
||||
{
|
||||
fn build_transform_op(
|
||||
&self,
|
||||
tokens: &mut VecDeque<Token>,
|
||||
|
@ -302,13 +320,11 @@ impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> BoxedTransform
|
|||
let inner_op = dict.try_build_statement(&mut inner_tokens)?;
|
||||
let (inner_op, op_transformed) = dict.try_build_one_transform(inner_op, tokens)?;
|
||||
if !op_transformed {
|
||||
return Err(
|
||||
SyntaxError {
|
||||
return Err(SyntaxError {
|
||||
token: Token::Name(INNER_VARIABLE_NAME.into()),
|
||||
got: Some(Token::Name(INNER_VARIABLE_NAME.into())),
|
||||
line: 0,
|
||||
}
|
||||
)
|
||||
});
|
||||
}
|
||||
//println!("Built 2nd filter: {}", inner_op);
|
||||
another_filter = Some(inner_op.into());
|
||||
|
@ -329,12 +345,10 @@ impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> BoxedTransform
|
|||
fn is_transform_op(&self, tokens: &VecDeque<Token>) -> bool {
|
||||
let result = if tokens.len() > 2 && tokens[0].is_dot() && tokens[1].is_open_bracket() {
|
||||
if check_name("if", &tokens[2]) {
|
||||
let tokens2: VecDeque<&Token> =
|
||||
VecDeque::from_iter(tokens.range(3..));
|
||||
let tokens2: VecDeque<&Token> = VecDeque::from_iter(tokens.range(3..));
|
||||
self.filter_factory.is_filter(&tokens2)
|
||||
} else {
|
||||
let tokens2: VecDeque<&Token> =
|
||||
VecDeque::from_iter(tokens.range(2..));
|
||||
let tokens2: VecDeque<&Token> = VecDeque::from_iter(tokens.range(2..));
|
||||
self.filter_factory.is_filter(&tokens2)
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -2,8 +2,8 @@ use std::collections::VecDeque;
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::iter::Iterator;
|
||||
|
||||
use crate::lang::SingleItem;
|
||||
use crate::lang::FilterPredicate;
|
||||
use crate::lang::SingleItem;
|
||||
use crate::lang::{IteratorItem, Op, PseudoOp};
|
||||
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp};
|
||||
use crate::processing::general::Type;
|
||||
|
@ -243,7 +243,11 @@ impl<P: FilterPredicate + 'static> Iterator for FilterReplaceStatement<P> {
|
|||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iterable.try_real_ref().map(|x| x.size_hint()).ok().unwrap_or((0, None))
|
||||
self.iterable
|
||||
.try_real_ref()
|
||||
.map(|x| x.size_hint())
|
||||
.ok()
|
||||
.unwrap_or((0, None))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,10 +42,7 @@ impl<O: Op + 'static, F: FunctionFactory<O> + 'static> BoxedOpFactory
|
|||
false
|
||||
} else {
|
||||
match &tokens[0] {
|
||||
Token::Name(n) => {
|
||||
self.op_factory.is_function(n)
|
||||
&& tokens[1].is_open_bracket()
|
||||
}
|
||||
Token::Name(n) => self.op_factory.is_function(n) && tokens[1].is_open_bracket(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
use std::iter::Iterator;
|
||||
|
||||
use crate::lang::{IteratorItem, Op, RuntimeError};
|
||||
use crate::lang::{RuntimeOp, RuntimeMsg, PseudoOp};
|
||||
use crate::lang::{PseudoOp, RuntimeMsg, RuntimeOp};
|
||||
use crate::Context;
|
||||
use crate::Item;
|
||||
|
||||
|
@ -12,7 +12,9 @@ pub struct GeneratorOp {
|
|||
}
|
||||
|
||||
impl GeneratorOp {
|
||||
pub fn new<F: (FnMut(&mut Context) -> Option<Result<Item, RuntimeMsg>>) + Send + 'static>(generator_fn: F) -> Self {
|
||||
pub fn new<F: (FnMut(&mut Context) -> Option<Result<Item, RuntimeMsg>>) + Send + 'static>(
|
||||
generator_fn: F,
|
||||
) -> Self {
|
||||
Self {
|
||||
context: None,
|
||||
generator: Box::new(generator_fn),
|
||||
|
@ -62,17 +64,15 @@ impl Op for GeneratorOp {
|
|||
}
|
||||
|
||||
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||
Err(
|
||||
RuntimeMsg("Cannot reset generator op".to_string())
|
||||
.with(RuntimeOp(PseudoOp::from_printable(self)))
|
||||
)
|
||||
Err(RuntimeMsg("Cannot reset generator op".to_string())
|
||||
.with(RuntimeOp(PseudoOp::from_printable(self))))
|
||||
}
|
||||
|
||||
fn dup(&self) -> Box<dyn Op> {
|
||||
// this shouldn't be called
|
||||
Box::new(Self {
|
||||
context: None,
|
||||
generator: Box::new(|_| None)
|
||||
generator: Box::new(|_| None),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -278,7 +278,7 @@ impl BoxedTransformOpFactory for ItemBlockFactory {
|
|||
got: tokens.pop_front(),
|
||||
token: Token::Literal(", or }".into()),
|
||||
line: 0,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
assert_token_raw(Token::CloseCurly, tokens)?;
|
||||
|
|
|
@ -29,7 +29,9 @@ pub use function::{FunctionFactory, FunctionStatementFactory};
|
|||
pub use generator_op::GeneratorOp;
|
||||
pub use iter_block::{ItemBlockFactory, ItemOp, ItemOpFactory};
|
||||
pub use lookup::Lookup;
|
||||
pub use operation::{BoxedOpFactory, IteratorItem, Op, OpFactory, SimpleOpFactory, BoxedTransformOpFactory};
|
||||
pub use operation::{
|
||||
BoxedOpFactory, BoxedTransformOpFactory, IteratorItem, Op, OpFactory, SimpleOpFactory,
|
||||
};
|
||||
pub use pseudo_op::PseudoOp;
|
||||
pub use repeated_meme::{repeated_tokens, RepeatedTokens};
|
||||
pub use single_op::SingleItem;
|
||||
|
@ -41,12 +43,10 @@ pub use vec_op::VecOp;
|
|||
pub mod vocabulary;
|
||||
|
||||
pub mod db {
|
||||
pub use super::db_items::{
|
||||
DbAlbumItem, DbArtistItem, DbGenreItem, DbMetaItem, DbMusicItem, DatabaseObj
|
||||
};
|
||||
#[cfg(feature = "sql")]
|
||||
pub use super::db_items::{generate_db, generate_default_db, DEFAULT_SQLITE_FILEPATH};
|
||||
pub use super::db_items::{
|
||||
generate_db, generate_default_db, DEFAULT_SQLITE_FILEPATH
|
||||
DatabaseObj, DbAlbumItem, DbArtistItem, DbGenreItem, DbMetaItem, DbMusicItem,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -37,11 +37,8 @@ impl<T: Op + 'static, X: SimpleOpFactory<T> + 'static> OpFactory<T> for X {
|
|||
pub trait OpFactory<T: Op + 'static> {
|
||||
fn is_op(&self, tokens: &TokenList) -> bool;
|
||||
|
||||
fn build_op(
|
||||
&self,
|
||||
tokens: &mut TokenList,
|
||||
dict: &LanguageDictionary,
|
||||
) -> Result<T, SyntaxError>;
|
||||
fn build_op(&self, tokens: &mut TokenList, dict: &LanguageDictionary)
|
||||
-> Result<T, SyntaxError>;
|
||||
|
||||
#[inline]
|
||||
fn build_box(
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::marker::PhantomData;
|
|||
|
||||
use crate::lang::utility::assert_token_raw;
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{IteratorItem, Op, PseudoOp, BoxedTransformOpFactory};
|
||||
use crate::lang::{BoxedTransformOpFactory, IteratorItem, Op, PseudoOp};
|
||||
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
|
||||
use crate::tokens::Token;
|
||||
use crate::Context;
|
||||
|
@ -149,12 +149,10 @@ impl<S: Sorter + 'static, F: SorterFactory<S> + 'static> BoxedTransformOpFactory
|
|||
|
||||
fn is_transform_op(&self, tokens: &VecDeque<Token>) -> bool {
|
||||
if tokens.len() > 2 {
|
||||
let tokens2: VecDeque<&Token> =
|
||||
VecDeque::from_iter(tokens.range(2..));
|
||||
let tokens2: VecDeque<&Token> = VecDeque::from_iter(tokens.range(2..));
|
||||
tokens[0].is_tilde() && self.sort_factory.is_sorter(&tokens2)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
use std::iter::Iterator;
|
||||
|
||||
use crate::lang::{IteratorItem, Op, RuntimeError};
|
||||
use crate::lang::{RuntimeOp, RuntimeMsg, PseudoOp};
|
||||
use crate::lang::{PseudoOp, RuntimeMsg, RuntimeOp};
|
||||
use crate::Context;
|
||||
use crate::Item;
|
||||
|
||||
|
|
|
@ -110,8 +110,7 @@ impl FieldFilterFactory<FieldFilter> for FieldFilterComparisonFactory {
|
|||
|| (tokens_len >= 2 // .field >= variable OR .field <= variable OR .field != variable OR .field == variable
|
||||
&& (tokens[0].is_open_angle_bracket() || tokens[0].is_close_angle_bracket() || tokens[0].is_equals() || tokens[0].is_exclamation())
|
||||
&& tokens[1].is_equals()
|
||||
&& !(tokens_len > 2 && tokens[2].is_equals())
|
||||
)
|
||||
&& !(tokens_len > 2 && tokens[2].is_equals()))
|
||||
}
|
||||
|
||||
fn build_filter(
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
|
||||
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
|
||||
use crate::tokens::Token;
|
||||
use crate::lang::utility::{assert_token, assert_token_raw};
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
use crate::tokens::Token;
|
||||
use crate::Context;
|
||||
use crate::Item;
|
||||
|
||||
|
@ -31,18 +31,18 @@ pub trait FieldFilterPredicate: Send + Sync + Debug + Display {
|
|||
}
|
||||
|
||||
pub struct FieldFilterFactoryBoxer<T: FieldFilterPredicate + 'static> {
|
||||
inner: Box<dyn FieldFilterFactory<T>>
|
||||
inner: Box<dyn FieldFilterFactory<T>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BoxedFilterPredicate {
|
||||
inner: Box<dyn FieldFilterPredicate + 'static>
|
||||
inner: Box<dyn FieldFilterPredicate + 'static>,
|
||||
}
|
||||
|
||||
impl Clone for BoxedFilterPredicate {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
inner: self.inner.box_clone()
|
||||
inner: self.inner.box_clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +85,9 @@ impl FieldFilterPredicate for BoxedFilterPredicate {
|
|||
}
|
||||
}
|
||||
|
||||
impl <T: FieldFilterPredicate + 'static> FieldFilterFactory<BoxedFilterPredicate> for FieldFilterFactoryBoxer<T> {
|
||||
impl<T: FieldFilterPredicate + 'static> FieldFilterFactory<BoxedFilterPredicate>
|
||||
for FieldFilterFactoryBoxer<T>
|
||||
{
|
||||
fn is_filter(&self, tokens: &[Token]) -> bool {
|
||||
self.inner.is_filter(tokens)
|
||||
}
|
||||
|
@ -96,7 +98,9 @@ impl <T: FieldFilterPredicate + 'static> FieldFilterFactory<BoxedFilterPredicate
|
|||
field: String,
|
||||
dict: &LanguageDictionary,
|
||||
) -> Result<BoxedFilterPredicate, SyntaxError> {
|
||||
self.inner.build_filter(tokens, field, dict).map(|x| BoxedFilterPredicate { inner: Box::new(x) })
|
||||
self.inner
|
||||
.build_filter(tokens, field, dict)
|
||||
.map(|x| BoxedFilterPredicate { inner: Box::new(x) })
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,10 +115,13 @@ impl FieldFilterBlockFactory {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn push<T: FieldFilterPredicate + 'static, F: FieldFilterFactory<T> + 'static>(mut self, factory: F) -> Self {
|
||||
self.field_filters.push(
|
||||
Box::new(FieldFilterFactoryBoxer { inner: Box::new(factory) })
|
||||
);
|
||||
pub fn push<T: FieldFilterPredicate + 'static, F: FieldFilterFactory<T> + 'static>(
|
||||
mut self,
|
||||
factory: F,
|
||||
) -> Self {
|
||||
self.field_filters.push(Box::new(FieldFilterFactoryBoxer {
|
||||
inner: Box::new(factory),
|
||||
}));
|
||||
self
|
||||
}
|
||||
|
||||
|
|
|
@ -2,10 +2,10 @@ use std::collections::VecDeque;
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
|
||||
use super::field_filter::{FieldFilterErrorHandling, VariableOrValue};
|
||||
use super::{FieldFilterFactory, FieldFilterPredicate};
|
||||
use crate::lang::utility::{assert_token, assert_token_raw, check_name};
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::TypePrimitive;
|
||||
use super::{FieldFilterFactory, FieldFilterPredicate};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
use crate::processing::general::Type;
|
||||
use crate::tokens::Token;
|
||||
|
|
|
@ -4,10 +4,10 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
use regex::{Regex, RegexBuilder};
|
||||
|
||||
use super::field_filter::{FieldFilterErrorHandling, VariableOrValue};
|
||||
use super::{FieldFilterFactory, FieldFilterPredicate};
|
||||
use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name};
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::TypePrimitive;
|
||||
use super::{FieldFilterFactory, FieldFilterPredicate};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
use crate::processing::general::Type;
|
||||
use crate::tokens::Token;
|
||||
|
|
|
@ -4,9 +4,7 @@ mod field_filter_maybe;
|
|||
mod field_like_filter;
|
||||
mod field_match_filter;
|
||||
|
||||
pub use field_filter::{
|
||||
FieldFilter, FieldFilterErrorHandling, FieldFilterComparisonFactory,
|
||||
};
|
||||
pub use field_filter::{FieldFilter, FieldFilterComparisonFactory, FieldFilterErrorHandling};
|
||||
pub use field_filter_maybe::FieldFilterMaybeFactory;
|
||||
pub use field_like_filter::FieldLikeFilterFactory;
|
||||
pub use field_match_filter::FieldRegexFilterFactory;
|
||||
|
|
|
@ -108,7 +108,8 @@ impl FilterFactory<UniqueFieldFilter> for UniqueFilterFactory {
|
|||
Token::Name("field_name".into()),
|
||||
tokens,
|
||||
)?;
|
||||
let error_handling = if !tokens.is_empty() && (tokens[0].is_exclamation() || tokens[0].is_interrogation()) {
|
||||
let error_handling =
|
||||
if !tokens.is_empty() && (tokens[0].is_exclamation() || tokens[0].is_interrogation()) {
|
||||
if tokens[0].is_exclamation() {
|
||||
assert_token_raw(Token::Exclamation, tokens)?;
|
||||
FieldFilterErrorHandling::Ignore
|
||||
|
|
|
@ -13,8 +13,7 @@ pub struct BracketsItemOpFactory;
|
|||
|
||||
impl ItemOpFactory<Box<dyn ItemOp>> for BracketsItemOpFactory {
|
||||
fn is_item_op(&self, tokens: &VecDeque<Token>) -> bool {
|
||||
!tokens.is_empty()
|
||||
&& tokens[0].is_open_bracket()
|
||||
!tokens.is_empty() && tokens[0].is_open_bracket()
|
||||
}
|
||||
|
||||
fn build_item_op(
|
||||
|
|
|
@ -123,7 +123,7 @@ fn find_first_comparison(tokens: &VecDeque<Token>) -> Option<usize> {
|
|||
{
|
||||
return Some(i);
|
||||
}
|
||||
},
|
||||
}
|
||||
Token::Comma if curly_depth == 0 && bracket_depth == 0 => {
|
||||
return None;
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@ use core::ops::Deref;
|
|||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
|
||||
use crate::lang::utility::{
|
||||
assert_name, assert_token, assert_token_raw, check_name,
|
||||
};
|
||||
use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name};
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{ItemBlockFactory, ItemOp, ItemOpFactory};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
|
|
|
@ -36,8 +36,7 @@ pub struct EmptyItemOpFactory;
|
|||
|
||||
impl ItemOpFactory<EmptyItemOp> for EmptyItemOpFactory {
|
||||
fn is_item_op(&self, tokens: &VecDeque<Token>) -> bool {
|
||||
!tokens.is_empty()
|
||||
&& check_name("empty", &tokens[0])
|
||||
!tokens.is_empty() && check_name("empty", &tokens[0])
|
||||
}
|
||||
|
||||
fn build_item_op(
|
||||
|
|
|
@ -27,7 +27,7 @@ impl Display for IterItemOp {
|
|||
fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
|
||||
match self.inner.lock() {
|
||||
Ok(inner) => write!(f, "iter {}", inner),
|
||||
Err(e) => write!(f, "iter !?!? (e:{})", e)
|
||||
Err(e) => write!(f, "iter !?!? (e:{})", e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ impl ItemOp for IterItemOp {
|
|||
fn execute(&self, _context: &mut Context) -> Result<Type, RuntimeMsg> {
|
||||
match self.inner.lock() {
|
||||
Ok(inner) => Ok(Type::Op(inner.dup())),
|
||||
Err(e) => Err(RuntimeMsg(format!("IterItemOp lock failed: {}", e)))
|
||||
Err(e) => Err(RuntimeMsg(format!("IterItemOp lock failed: {}", e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,8 @@ impl ItemOpFactory<IterItemOp> for IterItemOpFactory {
|
|||
) -> Result<IterItemOp, SyntaxError> {
|
||||
assert_name("iter", tokens)?;
|
||||
let inner_op = dict.try_build_statement(tokens)?;
|
||||
Ok(IterItemOp { inner: Mutex::new(inner_op) })
|
||||
Ok(IterItemOp {
|
||||
inner: Mutex::new(inner_op),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ use std::collections::VecDeque;
|
|||
|
||||
use crate::lang::utility::{assert_token, assert_token_raw};
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{ItemBlockFactory, ItemOpFactory};
|
||||
use crate::lang::SyntaxError;
|
||||
use crate::lang::{ItemBlockFactory, ItemOpFactory};
|
||||
use crate::tokens::Token;
|
||||
|
||||
use super::VariableRetrieveItemOp;
|
||||
|
@ -12,10 +12,7 @@ pub struct FieldRetrieveItemOpFactory;
|
|||
|
||||
impl ItemOpFactory<VariableRetrieveItemOp> for FieldRetrieveItemOpFactory {
|
||||
fn is_item_op(&self, tokens: &VecDeque<Token>) -> bool {
|
||||
tokens.len() > 2
|
||||
&& tokens[0].is_name()
|
||||
&& tokens[1].is_dot()
|
||||
&& tokens[2].is_name()
|
||||
tokens.len() > 2 && tokens[0].is_name() && tokens[1].is_dot() && tokens[2].is_name()
|
||||
}
|
||||
|
||||
fn build_item_op(
|
||||
|
|
|
@ -6,8 +6,8 @@ use crate::tokens::Token;
|
|||
use crate::Context;
|
||||
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op, GeneratorOp};
|
||||
use crate::lang::{PseudoOp, RuntimeError, RuntimeOp, SyntaxError, Lookup, TypePrimitive};
|
||||
use crate::lang::{FunctionFactory, FunctionStatementFactory, GeneratorOp, IteratorItem, Op};
|
||||
use crate::lang::{Lookup, PseudoOp, RuntimeError, RuntimeOp, SyntaxError, TypePrimitive};
|
||||
//use crate::processing::general::FileIter;
|
||||
use crate::processing::general::Type;
|
||||
|
||||
|
@ -51,13 +51,13 @@ impl Iterator for PlaylistStatement {
|
|||
let ctx = self.context.as_mut().unwrap();
|
||||
let file = match self.file.get(ctx) {
|
||||
Ok(Type::Primitive(TypePrimitive::String(s))) => s.to_owned(),
|
||||
Ok(x) => return Some(Err(
|
||||
RuntimeError {
|
||||
Ok(x) => {
|
||||
return Some(Err(RuntimeError {
|
||||
msg: format!("Cannot use {} as filepath", x),
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
}))
|
||||
}
|
||||
)),
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
|
||||
};
|
||||
let iter = ctx.filesystem.read_file(&file);
|
||||
|
@ -65,7 +65,7 @@ impl Iterator for PlaylistStatement {
|
|||
Ok(mut x) => {
|
||||
x.enter(self.context.take().unwrap());
|
||||
x
|
||||
},
|
||||
}
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
|
||||
});
|
||||
}
|
||||
|
@ -145,7 +145,8 @@ impl FunctionFactory<PlaylistStatement> for PlaylistFunctionFactory {
|
|||
}
|
||||
}
|
||||
|
||||
pub type PlaylistStatementFactory = FunctionStatementFactory<PlaylistStatement, PlaylistFunctionFactory>;
|
||||
pub type PlaylistStatementFactory =
|
||||
FunctionStatementFactory<PlaylistStatement, PlaylistFunctionFactory>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn playlist_function_factory() -> PlaylistStatementFactory {
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::collections::VecDeque;
|
|||
#[cfg(feature = "advanced")]
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
|
||||
use crate::lang::utility::{assert_name, check_name, assert_token_raw};
|
||||
use crate::lang::utility::{assert_name, assert_token_raw, check_name};
|
||||
use crate::lang::SyntaxError;
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::lang::{IteratorItem, Op, RuntimeMsg, Sorter};
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
#[cfg(feature = "advanced")]
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::lang::utility::{assert_name, check_name, assert_token_raw};
|
||||
use crate::lang::utility::{assert_name, assert_token_raw, check_name};
|
||||
use crate::lang::SyntaxError;
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::lang::{IteratorItem, Op, RuntimeMsg, Sorter};
|
||||
|
|
|
@ -4,16 +4,16 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::lang::utility::{assert_name, check_name, assert_token_raw, assert_token};
|
||||
use crate::lang::utility::{assert_name, assert_token, assert_token_raw, check_name};
|
||||
use crate::lang::SyntaxError;
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::lang::{IteratorItem, Op, RuntimeMsg, Sorter};
|
||||
use crate::lang::{LanguageDictionary, SortStatementFactory, SorterFactory};
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::processing::advanced::MusicAnalyzerDistance;
|
||||
use crate::tokens::Token;
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::Item;
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::processing::advanced::MusicAnalyzerDistance;
|
||||
|
||||
#[cfg(feature = "advanced")]
|
||||
#[derive(Debug)]
|
||||
|
@ -124,7 +124,6 @@ impl Sorter for RadioSorter {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if best_index != 0 {
|
||||
self.item_buf.swap(0, best_index);
|
||||
|
@ -170,9 +169,7 @@ pub struct RadioSorterFactory;
|
|||
|
||||
impl SorterFactory<RadioSorter> for RadioSorterFactory {
|
||||
fn is_sorter(&self, tokens: &VecDeque<&Token>) -> bool {
|
||||
tokens.len() > 1
|
||||
&& tokens[0].is_tilde()
|
||||
&& check_name("radio", tokens[1])
|
||||
tokens.len() > 1 && tokens[0].is_tilde() && check_name("radio", tokens[1])
|
||||
}
|
||||
|
||||
fn build_sorter(
|
||||
|
@ -184,31 +181,38 @@ impl SorterFactory<RadioSorter> for RadioSorterFactory {
|
|||
assert_name("radio", tokens)?;
|
||||
#[allow(dead_code)]
|
||||
let mode = if !tokens.is_empty() {
|
||||
Some(assert_token(|t| match t {
|
||||
Some(assert_token(
|
||||
|t| match t {
|
||||
Token::Name(n) => match &n as &str {
|
||||
"tempo" | "beat" => Some(MusicAnalyzerDistance::Tempo),
|
||||
"spectrum" | "s" => Some(MusicAnalyzerDistance::Spectrum),
|
||||
"loudness" | "volume" => Some(MusicAnalyzerDistance::Loudness),
|
||||
"chroma" | "c" => Some(MusicAnalyzerDistance::Chroma),
|
||||
_ => None
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}, Token::Name("".into()), tokens)?)
|
||||
},
|
||||
Token::Name("".into()),
|
||||
tokens,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
#[cfg(not(feature = "advanced"))]
|
||||
{Ok(RadioSorter::default())}
|
||||
{
|
||||
Ok(RadioSorter::default())
|
||||
}
|
||||
#[cfg(feature = "advanced")]
|
||||
{Ok(RadioSorter {
|
||||
{
|
||||
Ok(RadioSorter {
|
||||
comparison: mode,
|
||||
..Default::default()
|
||||
})}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type RadioSorterStatementFactory =
|
||||
SortStatementFactory<RadioSorter, RadioSorterFactory>;
|
||||
pub type RadioSorterStatementFactory = SortStatementFactory<RadioSorter, RadioSorterFactory>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn radio_sort() -> RadioSorterStatementFactory {
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
|
||||
use rand::{thread_rng, Rng};
|
||||
|
||||
use crate::lang::utility::{assert_name, check_name, assert_token_raw};
|
||||
use crate::lang::utility::{assert_name, assert_token_raw, check_name};
|
||||
use crate::lang::{IteratorItem, LanguageDictionary, Op};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
|
||||
|
|
|
@ -21,10 +21,12 @@ pub struct SqlStatement {
|
|||
|
||||
impl SqlStatement {
|
||||
fn get_item(&mut self) -> Option<IteratorItem> {
|
||||
let result = self.rows.as_mut().unwrap().next().map(|opt| opt.map_err(|mut e| {
|
||||
let result = self.rows.as_mut().unwrap().next().map(|opt| {
|
||||
opt.map_err(|mut e| {
|
||||
e.op = PseudoOp::from_printable(self);
|
||||
e
|
||||
}));
|
||||
})
|
||||
});
|
||||
if result.is_none() {
|
||||
self.is_complete = true;
|
||||
}
|
||||
|
@ -47,7 +49,6 @@ impl Op for SqlStatement {
|
|||
} else {
|
||||
self.rows.as_mut().unwrap().escape()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn is_resetable(&self) -> bool {
|
||||
|
@ -111,7 +112,10 @@ impl Iterator for SqlStatement {
|
|||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.rows.as_ref().map(|x| x.size_hint()).unwrap_or_default()
|
||||
self.rows
|
||||
.as_ref()
|
||||
.map(|x| x.size_hint())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -70,10 +70,12 @@ pub struct SimpleSqlStatement {
|
|||
|
||||
impl SimpleSqlStatement {
|
||||
fn get_item(&mut self) -> Option<IteratorItem> {
|
||||
let result = self.rows.as_mut().unwrap().next().map(|opt| opt.map_err(|mut e| {
|
||||
let result = self.rows.as_mut().unwrap().next().map(|opt| {
|
||||
opt.map_err(|mut e| {
|
||||
e.op = PseudoOp::from_printable(self);
|
||||
e
|
||||
}));
|
||||
})
|
||||
});
|
||||
if result.is_none() {
|
||||
self.is_complete = true;
|
||||
}
|
||||
|
@ -96,7 +98,6 @@ impl Op for SimpleSqlStatement {
|
|||
} else {
|
||||
self.rows.as_mut().unwrap().escape()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn is_resetable(&self) -> bool {
|
||||
|
@ -169,7 +170,10 @@ impl Iterator for SimpleSqlStatement {
|
|||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.rows.as_ref().map(|x| x.size_hint()).unwrap_or_default()
|
||||
self.rows
|
||||
.as_ref()
|
||||
.map(|x| x.size_hint())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -163,10 +163,12 @@ impl FunctionFactory<UnionStatement> for UnionFunctionFactory {
|
|||
) -> Result<UnionStatement, SyntaxError> {
|
||||
// union(op1, op2, ...)
|
||||
let operations = repeated_tokens(
|
||||
|tokens| if tokens[0].is_close_bracket() {
|
||||
|tokens| {
|
||||
if tokens[0].is_close_bracket() {
|
||||
Ok(None)
|
||||
} else {
|
||||
Ok(Some(PseudoOp::from(dict.try_build_statement(tokens)?)))
|
||||
}
|
||||
},
|
||||
Token::Comma,
|
||||
)
|
||||
|
|
|
@ -41,40 +41,62 @@ impl Iterator for VariableRetrieveStatement {
|
|||
if self.is_tried {
|
||||
return None;
|
||||
}
|
||||
let var = self.context.as_mut()
|
||||
let var = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables.remove(&self.variable_name)
|
||||
.variables
|
||||
.remove(&self.variable_name)
|
||||
.map_err(|e| e.with(RuntimeOp(PseudoOp::from_printable(self))));
|
||||
match var {
|
||||
Ok(Type::Op(mut op)) => {
|
||||
op.enter(self.context.take().unwrap());
|
||||
let next_item = op.next();
|
||||
self.enter(op.escape());
|
||||
if let Err(e) = self.context.as_mut().unwrap().variables.declare(&self.variable_name, Type::Op(op)) {
|
||||
if let Err(e) = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.declare(&self.variable_name, Type::Op(op))
|
||||
{
|
||||
return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))));
|
||||
}
|
||||
next_item
|
||||
},
|
||||
}
|
||||
Ok(Type::Item(item)) => {
|
||||
self.is_tried = true;
|
||||
if let Err(e) = self.context.as_mut().unwrap().variables.declare(&self.variable_name, Type::Item(item.clone())) {
|
||||
if let Err(e) = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.declare(&self.variable_name, Type::Item(item.clone()))
|
||||
{
|
||||
return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))));
|
||||
}
|
||||
Some(Ok(item))
|
||||
},
|
||||
}
|
||||
Ok(Type::Primitive(p)) => {
|
||||
self.is_tried = true;
|
||||
let err_msg = format!("Cannot iterate over primitive `{}` ({})", self.variable_name, p);
|
||||
if let Err(e) = self.context.as_mut().unwrap().variables.declare(&self.variable_name, Type::Primitive(p)) {
|
||||
let err_msg = format!(
|
||||
"Cannot iterate over primitive `{}` ({})",
|
||||
self.variable_name, p
|
||||
);
|
||||
if let Err(e) = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.declare(&self.variable_name, Type::Primitive(p))
|
||||
{
|
||||
return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))));
|
||||
}
|
||||
Some(Err(
|
||||
RuntimeError {
|
||||
Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: err_msg,
|
||||
}
|
||||
))
|
||||
}))
|
||||
}
|
||||
Err(e) => {
|
||||
self.is_tried = true;
|
||||
|
@ -114,9 +136,12 @@ impl Op for VariableRetrieveStatement {
|
|||
fn reset(&mut self) -> Result<(), RuntimeError> {
|
||||
self.is_tried = false;
|
||||
let runtime_op = RuntimeOp(PseudoOp::from_printable(self));
|
||||
let var = self.context.as_mut()
|
||||
let var = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables.get_mut(&self.variable_name)
|
||||
.variables
|
||||
.get_mut(&self.variable_name)
|
||||
.map_err(|e| e.with(runtime_op))?;
|
||||
|
||||
if let Type::Op(op) = var {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use symphonia::core::meta::{Value, Visual, Tag, StandardTagKey};
|
||||
use symphonia::core::meta::{StandardTagKey, Tag, Value, Visual};
|
||||
|
||||
use crate::lang::db::*;
|
||||
|
||||
|
@ -302,8 +302,7 @@ impl Tags {
|
|||
pub fn album_artist(&self, id: u64, genre_id: u64) -> DbArtistItem {
|
||||
DbArtistItem {
|
||||
artist_id: id,
|
||||
name: self.albumartist_name()
|
||||
.unwrap_or("Unknown Artist".into()),
|
||||
name: self.albumartist_name().unwrap_or("Unknown Artist".into()),
|
||||
genre: genre_id,
|
||||
}
|
||||
}
|
||||
|
@ -396,7 +395,11 @@ impl TagType {
|
|||
|
||||
#[inline]
|
||||
fn from_symphonia_visual(visual: &Visual) -> Option<Self> {
|
||||
Some(Self::Str(format!("data:{};base64,{}", &visual.media_type, base64::encode_config(&visual.data, BASE64_CONF))))
|
||||
Some(Self::Str(format!(
|
||||
"data:{};base64,{}",
|
||||
&visual.media_type,
|
||||
base64::encode_config(&visual.data, BASE64_CONF)
|
||||
)))
|
||||
}
|
||||
|
||||
fn str(&self) -> Option<&str> {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::fs::{DirEntry, ReadDir};
|
||||
use std::io::Read;
|
||||
use std::iter::Iterator;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::io::Read;
|
||||
|
||||
use regex::Regex;
|
||||
|
||||
use crate::lang::{RuntimeMsg, TypePrimitive, GeneratorOp};
|
||||
use crate::lang::{GeneratorOp, RuntimeMsg, TypePrimitive};
|
||||
use crate::Item;
|
||||
|
||||
const DEFAULT_REGEX: &str = r"/(?P<artist>[^/]+)/(?P<album>[^/]+)/(?:(?:(?P<disc>\d+)\s+)?(?P<track>\d+)\.?\s+)?(?P<title>[^/]+)\.(?P<format>(?:mp3)|(?:wav)|(?:ogg)|(?:flac)|(?:mp4)|(?:aac))$";
|
||||
|
@ -369,13 +369,24 @@ pub struct FilesystemExecutor {}
|
|||
|
||||
impl FilesystemExecutor {
|
||||
#[cfg(feature = "collections")]
|
||||
fn read_m3u8<P: AsRef<Path> + Send + 'static>(&self, path: P) -> Result<GeneratorOp, RuntimeMsg> {
|
||||
let mut file = std::fs::File::open(&path).map_err(|e| RuntimeMsg(format!("Path read error: {}", e)))?;
|
||||
fn read_m3u8<P: AsRef<Path> + Send + 'static>(
|
||||
&self,
|
||||
path: P,
|
||||
) -> Result<GeneratorOp, RuntimeMsg> {
|
||||
let mut file = std::fs::File::open(&path)
|
||||
.map_err(|e| RuntimeMsg(format!("Path read error: {}", e)))?;
|
||||
let mut file_bytes = Vec::new();
|
||||
file.read_to_end(&mut file_bytes).map_err(|e| RuntimeMsg(format!("File read error: {}", e)))?;
|
||||
let (_, playlist) = m3u8_rs::parse_playlist(&file_bytes).map_err(|e| RuntimeMsg(format!("Playlist read error: {}", e)))?;
|
||||
file.read_to_end(&mut file_bytes)
|
||||
.map_err(|e| RuntimeMsg(format!("File read error: {}", e)))?;
|
||||
let (_, playlist) = m3u8_rs::parse_playlist(&file_bytes)
|
||||
.map_err(|e| RuntimeMsg(format!("Playlist read error: {}", e)))?;
|
||||
let playlist = match playlist {
|
||||
m3u8_rs::Playlist::MasterPlaylist(_) => return Err(RuntimeMsg(format!("Playlist not supported: `{}` is a master (not media) playlist", path.as_ref().display()))),
|
||||
m3u8_rs::Playlist::MasterPlaylist(_) => {
|
||||
return Err(RuntimeMsg(format!(
|
||||
"Playlist not supported: `{}` is a master (not media) playlist",
|
||||
path.as_ref().display()
|
||||
)))
|
||||
}
|
||||
m3u8_rs::Playlist::MediaPlaylist(l) => l,
|
||||
};
|
||||
let mut index = 0;
|
||||
|
@ -388,7 +399,10 @@ impl FilesystemExecutor {
|
|||
if let Some(s) = joined_path.to_str() {
|
||||
s.to_owned()
|
||||
} else {
|
||||
return Some(Err(RuntimeMsg(format!("Failed to convert path to string for `{}`", joined_path.display()))));
|
||||
return Some(Err(RuntimeMsg(format!(
|
||||
"Failed to convert path to string for `{}`",
|
||||
joined_path.display()
|
||||
))));
|
||||
}
|
||||
} else {
|
||||
segment.uri.clone()
|
||||
|
@ -434,10 +448,17 @@ impl FilesystemQuerier for FilesystemExecutor {
|
|||
match &ext.to_lowercase() as &str {
|
||||
#[cfg(feature = "collections")]
|
||||
"m3u8" => self.read_m3u8(path),
|
||||
ext => Err(RuntimeMsg(format!("Unrecognised extension `{}` in path `{}`", ext, path.display())))
|
||||
ext => Err(RuntimeMsg(format!(
|
||||
"Unrecognised extension `{}` in path `{}`",
|
||||
ext,
|
||||
path.display()
|
||||
))),
|
||||
}
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Unrecognised path `{}`", path.display())))
|
||||
Err(RuntimeMsg(format!(
|
||||
"Unrecognised path `{}`",
|
||||
path.display()
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,13 +11,13 @@ mod variables;
|
|||
pub mod database {
|
||||
#[cfg(feature = "mpd")]
|
||||
pub use super::mpd::{MpdExecutor, MpdQuerier};
|
||||
pub use super::sql::{DatabaseQuerier, QueryResult};
|
||||
#[cfg(feature = "sql")]
|
||||
pub use super::sql::{SQLiteExecutor};
|
||||
#[cfg(feature = "fakesql")]
|
||||
pub use super::sql::{SQLiteTranspileExecutor};
|
||||
#[cfg(all(not(feature = "fakesql"), not(feature = "sql")))]
|
||||
pub use super::sql::{SQLErrExecutor};
|
||||
pub use super::sql::SQLErrExecutor;
|
||||
#[cfg(feature = "sql")]
|
||||
pub use super::sql::SQLiteExecutor;
|
||||
#[cfg(feature = "fakesql")]
|
||||
pub use super::sql::SQLiteTranspileExecutor;
|
||||
pub use super::sql::{DatabaseQuerier, QueryResult};
|
||||
}
|
||||
|
||||
pub mod general {
|
||||
|
|
|
@ -7,7 +7,7 @@ use std::sync::mpsc::{channel, Receiver, Sender};
|
|||
#[cfg(feature = "bliss-audio-symphonia")]
|
||||
use crate::lang::TypePrimitive;
|
||||
#[cfg(feature = "bliss-audio-symphonia")]
|
||||
use bliss_audio_symphonia::{BlissError, Song, AnalysisIndex};
|
||||
use bliss_audio_symphonia::{AnalysisIndex, BlissError, Song};
|
||||
|
||||
// assumed processor threads
|
||||
const DEFAULT_PARALLELISM: usize = 2;
|
||||
|
@ -38,7 +38,12 @@ pub trait MusicAnalyzer: Debug + Send {
|
|||
|
||||
fn get_distance(&mut self, from: &Item, to: &Item) -> Result<f64, RuntimeMsg>;
|
||||
|
||||
fn get_custom_distance(&mut self, from: &Item, to: &Item, compare: MusicAnalyzerDistance) -> Result<f64, RuntimeMsg>;
|
||||
fn get_custom_distance(
|
||||
&mut self,
|
||||
from: &Item,
|
||||
to: &Item,
|
||||
compare: MusicAnalyzerDistance,
|
||||
) -> Result<f64, RuntimeMsg>;
|
||||
|
||||
fn clear_cache(&mut self) -> Result<(), RuntimeMsg>;
|
||||
}
|
||||
|
@ -184,7 +189,12 @@ impl MusicAnalyzer for DefaultAnalyzer {
|
|||
))
|
||||
}
|
||||
|
||||
fn get_custom_distance(&mut self, from: &Item, to: &Item, compare: MusicAnalyzerDistance) -> Result<f64, RuntimeMsg> {
|
||||
fn get_custom_distance(
|
||||
&mut self,
|
||||
from: &Item,
|
||||
to: &Item,
|
||||
compare: MusicAnalyzerDistance,
|
||||
) -> Result<f64, RuntimeMsg> {
|
||||
self.request_song(from, true)?;
|
||||
self.request_song(to, true)?;
|
||||
let path_from = Self::get_path(from)?;
|
||||
|
@ -193,20 +203,19 @@ impl MusicAnalyzer for DefaultAnalyzer {
|
|||
let mut to_song = None;
|
||||
for response in self.responses.iter() {
|
||||
match response {
|
||||
ResponseType::Distance { .. } => {},
|
||||
ResponseType::Song {
|
||||
path,
|
||||
song
|
||||
} => {
|
||||
ResponseType::Distance { .. } => {}
|
||||
ResponseType::Song { path, song } => {
|
||||
if path_from == path {
|
||||
from_song = Some(song.map_err(|e| RuntimeMsg(format!("Bliss error: {}", e)))?);
|
||||
from_song =
|
||||
Some(song.map_err(|e| RuntimeMsg(format!("Bliss error: {}", e)))?);
|
||||
} else if path_to == path {
|
||||
to_song = Some(song.map_err(|e| RuntimeMsg(format!("Bliss error: {}", e)))?);
|
||||
to_song =
|
||||
Some(song.map_err(|e| RuntimeMsg(format!("Bliss error: {}", e)))?);
|
||||
}
|
||||
if to_song.is_some() && from_song.is_some() {
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
ResponseType::UnsupportedSong { path, msg } => {
|
||||
if path == path_to || path == path_from {
|
||||
return Err(RuntimeMsg(format!("Bliss error: {}", msg)));
|
||||
|
@ -218,36 +227,72 @@ impl MusicAnalyzer for DefaultAnalyzer {
|
|||
let to_arr = Self::bliss_song_to_array(&to_song.unwrap());
|
||||
let from_arr = Self::bliss_song_to_array(&from_song.unwrap());
|
||||
Ok(match compare {
|
||||
MusicAnalyzerDistance::Tempo => (
|
||||
(to_arr[AnalysisIndex::Tempo as usize] - from_arr[AnalysisIndex::Tempo as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Zcr as usize] - from_arr[AnalysisIndex::Zcr as usize]).powi(2)
|
||||
).sqrt(),
|
||||
MusicAnalyzerDistance::Spectrum => (
|
||||
(to_arr[AnalysisIndex::MeanSpectralCentroid as usize] - from_arr[AnalysisIndex::MeanSpectralCentroid as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::StdDeviationSpectralCentroid as usize] - from_arr[AnalysisIndex::StdDeviationSpectralCentroid as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::MeanSpectralRolloff as usize] - from_arr[AnalysisIndex::MeanSpectralRolloff as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::StdDeviationSpectralRolloff as usize] - from_arr[AnalysisIndex::StdDeviationSpectralRolloff as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::MeanSpectralFlatness as usize] - from_arr[AnalysisIndex::MeanSpectralFlatness as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::StdDeviationSpectralFlatness as usize] - from_arr[AnalysisIndex::StdDeviationSpectralFlatness as usize]).powi(2)
|
||||
).sqrt(),
|
||||
MusicAnalyzerDistance::Tempo => ((to_arr[AnalysisIndex::Tempo as usize]
|
||||
- from_arr[AnalysisIndex::Tempo as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Zcr as usize]
|
||||
- from_arr[AnalysisIndex::Zcr as usize])
|
||||
.powi(2))
|
||||
.sqrt(),
|
||||
MusicAnalyzerDistance::Spectrum => ((to_arr
|
||||
[AnalysisIndex::MeanSpectralCentroid as usize]
|
||||
- from_arr[AnalysisIndex::MeanSpectralCentroid as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::StdDeviationSpectralCentroid as usize]
|
||||
- from_arr[AnalysisIndex::StdDeviationSpectralCentroid as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::MeanSpectralRolloff as usize]
|
||||
- from_arr[AnalysisIndex::MeanSpectralRolloff as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::StdDeviationSpectralRolloff as usize]
|
||||
- from_arr[AnalysisIndex::StdDeviationSpectralRolloff as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::MeanSpectralFlatness as usize]
|
||||
- from_arr[AnalysisIndex::MeanSpectralFlatness as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::StdDeviationSpectralFlatness as usize]
|
||||
- from_arr[AnalysisIndex::StdDeviationSpectralFlatness as usize])
|
||||
.powi(2))
|
||||
.sqrt(),
|
||||
MusicAnalyzerDistance::Loudness => {
|
||||
let mean_delta = to_arr[AnalysisIndex::MeanLoudness as usize] - from_arr[AnalysisIndex::MeanLoudness as usize];
|
||||
let deviation_delta = to_arr[AnalysisIndex::StdDeviationLoudness as usize] - from_arr[AnalysisIndex::StdDeviationLoudness as usize];
|
||||
let mean_delta = to_arr[AnalysisIndex::MeanLoudness as usize]
|
||||
- from_arr[AnalysisIndex::MeanLoudness as usize];
|
||||
let deviation_delta = to_arr[AnalysisIndex::StdDeviationLoudness as usize]
|
||||
- from_arr[AnalysisIndex::StdDeviationLoudness as usize];
|
||||
|
||||
(mean_delta.powi(2) + deviation_delta.powi(2)).sqrt()
|
||||
},
|
||||
MusicAnalyzerDistance::Chroma => (
|
||||
(to_arr[AnalysisIndex::Chroma1 as usize] - from_arr[AnalysisIndex::Chroma1 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma2 as usize] - from_arr[AnalysisIndex::Chroma2 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma3 as usize] - from_arr[AnalysisIndex::Chroma3 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma4 as usize] - from_arr[AnalysisIndex::Chroma4 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma5 as usize] - from_arr[AnalysisIndex::Chroma5 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma6 as usize] - from_arr[AnalysisIndex::Chroma6 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma7 as usize] - from_arr[AnalysisIndex::Chroma7 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma8 as usize] - from_arr[AnalysisIndex::Chroma8 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma9 as usize] - from_arr[AnalysisIndex::Chroma9 as usize]).powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma10 as usize] - from_arr[AnalysisIndex::Chroma10 as usize]).powi(2)
|
||||
).sqrt(),
|
||||
}
|
||||
MusicAnalyzerDistance::Chroma => ((to_arr[AnalysisIndex::Chroma1 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma1 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma2 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma2 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma3 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma3 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma4 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma4 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma5 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma5 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma6 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma6 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma7 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma7 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma8 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma8 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma9 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma9 as usize])
|
||||
.powi(2)
|
||||
+ (to_arr[AnalysisIndex::Chroma10 as usize]
|
||||
- from_arr[AnalysisIndex::Chroma10 as usize])
|
||||
.powi(2))
|
||||
.sqrt(),
|
||||
})
|
||||
} else {
|
||||
Err(RuntimeMsg(
|
||||
|
@ -281,7 +326,12 @@ impl MusicAnalyzer for DefaultAnalyzer {
|
|||
Ok(f64::MAX)
|
||||
}
|
||||
|
||||
fn get_custom_distance(&mut self, _from: &Item, _to: &Item, _compare: MusicAnalyzerDistance) -> Result<f64, RuntimeMsg> {
|
||||
fn get_custom_distance(
|
||||
&mut self,
|
||||
_from: &Item,
|
||||
_to: &Item,
|
||||
_compare: MusicAnalyzerDistance,
|
||||
) -> Result<f64, RuntimeMsg> {
|
||||
Ok(f64::MAX)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,8 +8,8 @@ use std::fmt::Write;
|
|||
|
||||
#[cfg(feature = "sql")]
|
||||
use crate::lang::db::*;
|
||||
use crate::lang::RuntimeMsg;
|
||||
use crate::lang::Op;
|
||||
use crate::lang::RuntimeMsg;
|
||||
#[cfg(feature = "sql")]
|
||||
use crate::lang::VecOp;
|
||||
#[cfg(feature = "sql")]
|
||||
|
@ -68,10 +68,14 @@ impl SQLiteExecutor {
|
|||
self.gen_db_maybe()?;
|
||||
let conn = self.sqlite_connection.as_mut().unwrap();
|
||||
match perform_single_param_query(conn, query, param) {
|
||||
Ok(items) => Ok(Box::new(VecOp::from(items
|
||||
Ok(items) => Ok(Box::new(VecOp::from(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|item| item.map_err(|e| RuntimeMsg(format!("SQL item mapping error: {}", e))))
|
||||
.collect::<Vec<_>>()))),
|
||||
.map(|item| {
|
||||
item.map_err(|e| RuntimeMsg(format!("SQL item mapping error: {}", e)))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
))),
|
||||
Err(e) => Err(RuntimeMsg(e)),
|
||||
}
|
||||
}
|
||||
|
@ -84,10 +88,14 @@ impl DatabaseQuerier for SQLiteExecutor {
|
|||
let conn = self.sqlite_connection.as_mut().unwrap();
|
||||
// execute query
|
||||
match perform_query(conn, query) {
|
||||
Ok(items) => Ok(Box::new(VecOp::from(items
|
||||
Ok(items) => Ok(Box::new(VecOp::from(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|item| item.map_err(|e| RuntimeMsg(format!("SQL item mapping error: {}", e))))
|
||||
.collect::<Vec<_>>()))),
|
||||
.map(|item| {
|
||||
item.map_err(|e| RuntimeMsg(format!("SQL item mapping error: {}", e)))
|
||||
})
|
||||
.collect::<Vec<_>>(),
|
||||
))),
|
||||
Err(e) => Err(RuntimeMsg(e)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::iter::Iterator;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use sqlparser::{parser::Parser, dialect::SQLiteDialect};
|
||||
use sqlparser::ast::{Statement, SetExpr, Expr, OrderByExpr, Value, BinaryOperator};
|
||||
use sqlparser::ast::{BinaryOperator, Expr, OrderByExpr, SetExpr, Statement, Value};
|
||||
use sqlparser::{dialect::SQLiteDialect, parser::Parser};
|
||||
|
||||
use crate::Context;
|
||||
|
||||
use crate::lang::{IteratorItem, Op, PseudoOp};
|
||||
use crate::lang::{RuntimeError, RuntimeOp, RuntimeMsg, TypePrimitive};
|
||||
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp, TypePrimitive};
|
||||
use crate::processing::general::FileIter;
|
||||
use crate::Item;
|
||||
|
||||
|
@ -25,7 +25,8 @@ pub struct RawSqlQuery {
|
|||
|
||||
impl RawSqlQuery {
|
||||
pub fn emit(query_str: &str) -> Result<Self, RuntimeMsg> {
|
||||
let mut statements = Parser::parse_sql(&SQLiteDialect{}, query_str).map_err(|e| RuntimeMsg(format!("Could not parse SQL query: {}", e)))?;
|
||||
let mut statements = Parser::parse_sql(&SQLiteDialect {}, query_str)
|
||||
.map_err(|e| RuntimeMsg(format!("Could not parse SQL query: {}", e)))?;
|
||||
if statements.len() == 1 {
|
||||
if let Statement::Query(mut query) = statements.remove(0) {
|
||||
let matching = if let SetExpr::Select(select) = *query.body {
|
||||
|
@ -55,7 +56,10 @@ impl RawSqlQuery {
|
|||
Err(RuntimeMsg("Expected SQL SELECT statement".to_owned()))
|
||||
}
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Expected exactly 1 SQL SELECT statement, got {} statements", statements.len())))
|
||||
Err(RuntimeMsg(format!(
|
||||
"Expected exactly 1 SQL SELECT statement, got {} statements",
|
||||
statements.len()
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -99,11 +103,12 @@ impl Iterator for RawSqlQuery {
|
|||
} else {
|
||||
self.has_tried = true;
|
||||
}
|
||||
let iter = self.context.as_mut().unwrap().filesystem.raw(
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
let iter = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.filesystem
|
||||
.raw(None, None, true);
|
||||
self.file_iter = Some(match iter {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
|
||||
|
@ -118,12 +123,12 @@ impl Iterator for RawSqlQuery {
|
|||
if self.matches_filters(&item) {
|
||||
self.items_buffer.push_back(Ok(item));
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => self.items_buffer.push_back(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: e,
|
||||
}))
|
||||
})),
|
||||
}
|
||||
}
|
||||
let new_len = self.items_buffer.len();
|
||||
|
@ -140,20 +145,25 @@ impl Iterator for RawSqlQuery {
|
|||
if self.matches_filters(&item) {
|
||||
return Some(Ok(item));
|
||||
}
|
||||
},
|
||||
Err(e) => return Some(Err(RuntimeError {
|
||||
}
|
||||
Err(e) => {
|
||||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: e,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.file_iter.as_ref().map(|x| x.size_hint()).unwrap_or_default()
|
||||
self.file_iter
|
||||
.as_ref()
|
||||
.map(|x| x.size_hint())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,25 +191,51 @@ impl Op for RawSqlQuery {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
enum MatchRule {
|
||||
Like { field: String, pattern: LikePattern, negated: bool },
|
||||
CompareVal { field: String, value: TypePrimitive, comparison: [i8; 2] },
|
||||
CompareFields { field_a: String, field_b: String, comparison: [i8; 2] },
|
||||
And { a: Box<MatchRule>, b: Box<MatchRule> },
|
||||
Or { a: Box<MatchRule>, b: Box<MatchRule> },
|
||||
Like {
|
||||
field: String,
|
||||
pattern: LikePattern,
|
||||
negated: bool,
|
||||
},
|
||||
CompareVal {
|
||||
field: String,
|
||||
value: TypePrimitive,
|
||||
comparison: [i8; 2],
|
||||
},
|
||||
CompareFields {
|
||||
field_a: String,
|
||||
field_b: String,
|
||||
comparison: [i8; 2],
|
||||
},
|
||||
And {
|
||||
a: Box<MatchRule>,
|
||||
b: Box<MatchRule>,
|
||||
},
|
||||
Or {
|
||||
a: Box<MatchRule>,
|
||||
b: Box<MatchRule>,
|
||||
},
|
||||
}
|
||||
|
||||
impl MatchRule {
|
||||
#[inline]
|
||||
fn is_match(&self, item: &Item) -> bool {
|
||||
match self {
|
||||
Self::Like { field, pattern, negated } => {
|
||||
Self::Like {
|
||||
field,
|
||||
pattern,
|
||||
negated,
|
||||
} => {
|
||||
if let Some(TypePrimitive::String(val)) = item.field(field) {
|
||||
pattern.is_match(val) != *negated
|
||||
} else {
|
||||
*negated
|
||||
}
|
||||
},
|
||||
Self::CompareVal { field, value, comparison } => {
|
||||
}
|
||||
Self::CompareVal {
|
||||
field,
|
||||
value,
|
||||
comparison,
|
||||
} => {
|
||||
if let Some(val) = item.field(field) {
|
||||
match val.compare(value) {
|
||||
Ok(cmp) => comparison[0] == cmp || comparison[1] == cmp,
|
||||
|
@ -211,8 +247,12 @@ impl MatchRule {
|
|||
Err(_) => comparison[0] != 0 && comparison[1] != 0,
|
||||
}
|
||||
}
|
||||
},
|
||||
Self::CompareFields { field_a, field_b, comparison} => {
|
||||
}
|
||||
Self::CompareFields {
|
||||
field_a,
|
||||
field_b,
|
||||
comparison,
|
||||
} => {
|
||||
if let Some(val_a) = item.field(field_a) {
|
||||
if let Some(val_b) = item.field(field_b) {
|
||||
match val_a.compare(val_b) {
|
||||
|
@ -238,84 +278,163 @@ impl MatchRule {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
Self::And { a, b } => {
|
||||
a.is_match(item) && b.is_match(item)
|
||||
},
|
||||
Self::Or { a, b } => {
|
||||
a.is_match(item) || b.is_match(item)
|
||||
},
|
||||
}
|
||||
Self::And { a, b } => a.is_match(item) && b.is_match(item),
|
||||
Self::Or { a, b } => a.is_match(item) || b.is_match(item),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn from_parsed(expr: Expr) -> Result<Self, RuntimeMsg> {
|
||||
match expr {
|
||||
Expr::IsFalse(x) => if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal{ field: id.value, value:TypePrimitive::Bool(false), comparison: [0, 0] })
|
||||
Expr::IsFalse(x) => {
|
||||
if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal {
|
||||
field: id.value,
|
||||
value: TypePrimitive::Bool(false),
|
||||
comparison: [0, 0],
|
||||
})
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Unsupported SQL IS FALSE syntax: {}", x)))
|
||||
},
|
||||
Expr::IsNotFalse(x) => if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal{ field: id.value, value:TypePrimitive::Bool(false), comparison: [1, -1] })
|
||||
Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL IS FALSE syntax: {}",
|
||||
x
|
||||
)))
|
||||
}
|
||||
}
|
||||
Expr::IsNotFalse(x) => {
|
||||
if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal {
|
||||
field: id.value,
|
||||
value: TypePrimitive::Bool(false),
|
||||
comparison: [1, -1],
|
||||
})
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Unsupported SQL IS NOT FALSE syntax: {}", x)))
|
||||
},
|
||||
Expr::IsTrue(x) => if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal{ field: id.value, value:TypePrimitive::Bool(true), comparison: [0, 0] })
|
||||
Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL IS NOT FALSE syntax: {}",
|
||||
x
|
||||
)))
|
||||
}
|
||||
}
|
||||
Expr::IsTrue(x) => {
|
||||
if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal {
|
||||
field: id.value,
|
||||
value: TypePrimitive::Bool(true),
|
||||
comparison: [0, 0],
|
||||
})
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Unsupported SQL IS TRUE syntax: {}", x)))
|
||||
},
|
||||
Expr::IsNotTrue(x) => if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal{ field: id.value, value:TypePrimitive::Bool(true), comparison: [1, -1] })
|
||||
}
|
||||
}
|
||||
Expr::IsNotTrue(x) => {
|
||||
if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal {
|
||||
field: id.value,
|
||||
value: TypePrimitive::Bool(true),
|
||||
comparison: [1, -1],
|
||||
})
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Unsupported SQL IS NOT TRUE syntax: {}", x)))
|
||||
},
|
||||
Expr::IsNull(x) => if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal{ field: id.value, value:TypePrimitive::Empty, comparison: [0, 0] })
|
||||
Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL IS NOT TRUE syntax: {}",
|
||||
x
|
||||
)))
|
||||
}
|
||||
}
|
||||
Expr::IsNull(x) => {
|
||||
if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal {
|
||||
field: id.value,
|
||||
value: TypePrimitive::Empty,
|
||||
comparison: [0, 0],
|
||||
})
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Unsupported SQL IS NULL syntax: {}", x)))
|
||||
},
|
||||
Expr::IsNotNull(x) => if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal{ field: id.value, value:TypePrimitive::Empty, comparison: [1, -1] })
|
||||
}
|
||||
}
|
||||
Expr::IsNotNull(x) => {
|
||||
if let Expr::Identifier(id) = *x {
|
||||
Ok(Self::CompareVal {
|
||||
field: id.value,
|
||||
value: TypePrimitive::Empty,
|
||||
comparison: [1, -1],
|
||||
})
|
||||
} else {
|
||||
Err(RuntimeMsg(format!("Unsupported SQL IS NOT NULL syntax: {}", x)))
|
||||
Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL IS NOT NULL syntax: {}",
|
||||
x
|
||||
)))
|
||||
}
|
||||
}
|
||||
Expr::Like {
|
||||
negated,
|
||||
expr,
|
||||
pattern,
|
||||
..
|
||||
} => match (*expr, *pattern) {
|
||||
(Expr::Identifier(expr), Expr::Value(Value::SingleQuotedString(pattern))) => {
|
||||
Ok(Self::Like {
|
||||
field: expr.value,
|
||||
negated: negated,
|
||||
pattern: LikePattern::from_string(pattern),
|
||||
})
|
||||
}
|
||||
(x, y) => Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL LIKE syntax: {} LIKE {}",
|
||||
x, y
|
||||
))),
|
||||
},
|
||||
Expr::Like { negated, expr, pattern, .. } => match (*expr, *pattern) {
|
||||
(Expr::Identifier(expr), Expr::Value(Value::SingleQuotedString(pattern))) =>
|
||||
Ok(Self::Like{ field: expr.value, negated: negated, pattern: LikePattern::from_string(pattern) }),
|
||||
(x, y) => Err(RuntimeMsg(format!("Unsupported SQL LIKE syntax: {} LIKE {}", x, y)))
|
||||
},
|
||||
Expr::ILike { negated, expr, pattern, .. } => match (*expr, *pattern) {
|
||||
(Expr::Identifier(expr), Expr::Value(Value::SingleQuotedString(pattern))) =>
|
||||
Ok(Self::Like{ field: expr.value, negated: negated, pattern: LikePattern::from_string(pattern) }),
|
||||
(x, y) => Err(RuntimeMsg(format!("Unsupported SQL ILIKE syntax: {} ILIKE {}", x, y)))
|
||||
Expr::ILike {
|
||||
negated,
|
||||
expr,
|
||||
pattern,
|
||||
..
|
||||
} => match (*expr, *pattern) {
|
||||
(Expr::Identifier(expr), Expr::Value(Value::SingleQuotedString(pattern))) => {
|
||||
Ok(Self::Like {
|
||||
field: expr.value,
|
||||
negated: negated,
|
||||
pattern: LikePattern::from_string(pattern),
|
||||
})
|
||||
}
|
||||
(x, y) => Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL ILIKE syntax: {} ILIKE {}",
|
||||
x, y
|
||||
))),
|
||||
},
|
||||
Expr::Nested(x) => Self::from_parsed(*x),
|
||||
Expr::BinaryOp { left, op, right } => {
|
||||
if let BinaryOperator::And = op {
|
||||
Ok(Self::And { a: Box::new(Self::from_parsed(*left)?), b: Box::new(Self::from_parsed(*right)?) })
|
||||
Ok(Self::And {
|
||||
a: Box::new(Self::from_parsed(*left)?),
|
||||
b: Box::new(Self::from_parsed(*right)?),
|
||||
})
|
||||
} else if let BinaryOperator::Or = op {
|
||||
Ok(Self::Or { a: Box::new(Self::from_parsed(*left)?), b: Box::new(Self::from_parsed(*right)?) })
|
||||
Ok(Self::Or {
|
||||
a: Box::new(Self::from_parsed(*left)?),
|
||||
b: Box::new(Self::from_parsed(*right)?),
|
||||
})
|
||||
} else {
|
||||
match (*left, *right) {
|
||||
(Expr::Identifier(left), Expr::Value(right)) =>
|
||||
Ok(Self::CompareVal {
|
||||
(Expr::Identifier(left), Expr::Value(right)) => Ok(Self::CompareVal {
|
||||
field: left.value,
|
||||
value: value_to_primitive(right)?,
|
||||
comparison: binary_op_to_compare(op)?,
|
||||
}),
|
||||
(Expr::Identifier(left), Expr::Identifier(right)) =>
|
||||
(Expr::Identifier(left), Expr::Identifier(right)) => {
|
||||
Ok(Self::CompareFields {
|
||||
field_a: left.value,
|
||||
field_b: right.value,
|
||||
comparison: binary_op_to_compare(op)?,
|
||||
}),
|
||||
(x, y) => Err(RuntimeMsg(format!("Unsupported SQL operator syntax: {} {} {}", x, op, y)))
|
||||
})
|
||||
}
|
||||
(x, y) => Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL operator syntax: {} {} {}",
|
||||
x, op, y
|
||||
))),
|
||||
}
|
||||
}
|
||||
},
|
||||
x => Err(RuntimeMsg(format!("Unsupported SQL WHERE syntax: {}", x)))
|
||||
}
|
||||
x => Err(RuntimeMsg(format!("Unsupported SQL WHERE syntax: {}", x))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,7 +448,10 @@ fn binary_op_to_compare(op: BinaryOperator) -> Result<[i8; 2], RuntimeMsg> {
|
|||
BinaryOperator::LtEq => Ok([-1, 0]),
|
||||
BinaryOperator::Eq => Ok([0, 0]),
|
||||
BinaryOperator::NotEq => Ok([-1, 1]),
|
||||
x => Err(RuntimeMsg(format!("Unsupported SQL operator syntax: {}", x)))
|
||||
x => Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL operator syntax: {}",
|
||||
x
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,7 +463,10 @@ fn value_to_primitive(val: Value) -> Result<TypePrimitive, RuntimeMsg> {
|
|||
Value::DoubleQuotedString(s) => Ok(TypePrimitive::String(s)),
|
||||
Value::Boolean(b) => Ok(TypePrimitive::Bool(b)),
|
||||
Value::Null => Ok(TypePrimitive::Empty),
|
||||
x => Err(RuntimeMsg(format!("Unsupported SQL operator syntax: {}", x)))
|
||||
x => Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL operator syntax: {}",
|
||||
x
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -367,9 +492,9 @@ impl LikePattern {
|
|||
#[inline]
|
||||
fn from_string(pattern: String) -> Self {
|
||||
match (pattern.starts_with('%'), pattern.ends_with('%')) {
|
||||
(false, true) => Self::EndsWith(pattern[..pattern.len()-1].to_owned()),
|
||||
(false, true) => Self::EndsWith(pattern[..pattern.len() - 1].to_owned()),
|
||||
(true, false) => Self::StartWith(pattern[1..].to_owned()),
|
||||
(true, true) => Self::Contains(pattern[1..pattern.len()-1].to_owned()),
|
||||
(true, true) => Self::Contains(pattern[1..pattern.len() - 1].to_owned()),
|
||||
(false, false) => Self::Is(pattern),
|
||||
}
|
||||
}
|
||||
|
@ -402,7 +527,7 @@ impl SortRule {
|
|||
}
|
||||
std::cmp::Ordering::Equal
|
||||
});
|
||||
},
|
||||
}
|
||||
Self::Descending(field) => {
|
||||
buffer.sort_by(|a, b| {
|
||||
if let Ok(a) = a {
|
||||
|
@ -427,7 +552,9 @@ impl SortRule {
|
|||
let field = if let Expr::Identifier(id) = order.expr {
|
||||
id.value
|
||||
} else {
|
||||
return Err(RuntimeMsg(format!("Unsupported SQL syntax: ORDER BY value must be a field identifier")));
|
||||
return Err(RuntimeMsg(format!(
|
||||
"Unsupported SQL syntax: ORDER BY value must be a field identifier"
|
||||
)));
|
||||
};
|
||||
if order.asc.unwrap_or(true) {
|
||||
Ok(Self::Ascending(field))
|
||||
|
|
|
@ -56,11 +56,12 @@ impl Iterator for SimpleSqlQuery {
|
|||
} else {
|
||||
self.has_tried = true;
|
||||
}
|
||||
let iter = self.context.as_mut().unwrap().filesystem.raw(
|
||||
None,
|
||||
None,
|
||||
true,
|
||||
);
|
||||
let iter = self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.filesystem
|
||||
.raw(None, None, true);
|
||||
self.file_iter = Some(match iter {
|
||||
Ok(x) => x,
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
|
||||
|
@ -71,23 +72,30 @@ impl Iterator for SimpleSqlQuery {
|
|||
Ok(item) => {
|
||||
// apply filter
|
||||
if let Some(TypePrimitive::String(field_val)) = item.field(&self.field_name) {
|
||||
if crate::lang::vocabulary::filters::utility::sanitise_string(field_val).contains(&self.val) {
|
||||
if crate::lang::vocabulary::filters::utility::sanitise_string(field_val)
|
||||
.contains(&self.val)
|
||||
{
|
||||
return Some(Ok(item));
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => return Some(Err(RuntimeError {
|
||||
}
|
||||
Err(e) => {
|
||||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: e,
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.file_iter.as_ref().map(|x| x.size_hint()).unwrap_or_default()
|
||||
self.file_iter
|
||||
.as_ref()
|
||||
.map(|x| x.size_hint())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,11 @@ fn execute_single_line(
|
|||
|
||||
#[test]
|
||||
fn execute_sql_line() -> Result<(), InterpreterError> {
|
||||
execute_single_line("sql(`SELECT * FROM songs WHERE artist IS NOT NULL ORDER BY artist;`)", false, true)?;
|
||||
execute_single_line(
|
||||
"sql(`SELECT * FROM songs WHERE artist IS NOT NULL ORDER BY artist;`)",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line("sql(`SELECT * FROM songs WHERE artist IS NOT NULL AND format = 'flac' ORDER BY title DESC;`)", false, true)
|
||||
}
|
||||
|
||||
|
@ -873,11 +877,7 @@ fn execute_mpdfunction_line() -> Result<(), InterpreterError> {
|
|||
|
||||
#[test]
|
||||
fn execute_playlist_line() -> Result<(), InterpreterError> {
|
||||
execute_single_line(
|
||||
r"playlist(`~/Music/Playlists/cabello.m3u8`)",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line(r"playlist(`~/Music/Playlists/cabello.m3u8`)", false, true)?;
|
||||
execute_single_line(
|
||||
r"playlist(`/home/ngnius/Music/Playlists/powers.m3u8`)",
|
||||
false,
|
||||
|
|
|
@ -8,7 +8,7 @@ readme = "README.md"
|
|||
[dependencies]
|
||||
rodio = { version = "^0.16", features = ["symphonia-all"], default-features = false}
|
||||
m3u8-rs = { version = "^3.0" }
|
||||
mpd = { version = "0.0.12", optional = true }
|
||||
mpd = { version = "0.1", optional = true }
|
||||
|
||||
# local
|
||||
muss-interpreter = { path = "../interpreter", version = "0.9.0" }
|
||||
|
|
|
@ -14,8 +14,13 @@ impl PlayerError {
|
|||
Self::Playback(PlaybackError::from_err(err))
|
||||
}
|
||||
|
||||
pub(crate) fn from_file_err_playback<E: Display, P: AsRef<std::path::Path>>(err: E, path: P) -> Self {
|
||||
Self::Playback(PlaybackError { msg: format!("{}: `{}`", err, path.as_ref().display()) })
|
||||
pub(crate) fn from_file_err_playback<E: Display, P: AsRef<std::path::Path>>(
|
||||
err: E,
|
||||
path: P,
|
||||
) -> Self {
|
||||
Self::Playback(PlaybackError {
|
||||
msg: format!("{}: `{}`", err, path.as_ref().display()),
|
||||
})
|
||||
}
|
||||
|
||||
/*pub(crate) fn from_err_uri<E: Display>(err: E) -> Self {
|
||||
|
|
|
@ -148,7 +148,7 @@ impl SystemControlWrapper {
|
|||
Ok(DbusControl::Die) => break,
|
||||
Ok(DbusControl::SetMetadata(meta)) => {
|
||||
dbus_conn.set_metadata(meta);
|
||||
},
|
||||
}
|
||||
Ok(DbusControl::SetPosition(pos)) => {
|
||||
dbus_conn.set_position(pos);
|
||||
}
|
||||
|
@ -171,16 +171,21 @@ impl SystemControlWrapper {
|
|||
playback_time = 0;
|
||||
duration_cache = None;
|
||||
Self::enqueued(item, &dbus_ctrl_tx_clone);
|
||||
},
|
||||
}
|
||||
Ok(PlaybackAction::Empty) => Self::empty(&dbus_ctrl_tx_clone),
|
||||
Ok(PlaybackAction::Time(item, duration)) => {
|
||||
duration_cache = Some(duration);
|
||||
Self::time(item, duration, &dbus_ctrl_tx_clone);
|
||||
},
|
||||
}
|
||||
Ok(PlaybackAction::UpdateTick(item)) => {
|
||||
Self::time_update(item, playback_time, &duration_cache, &dbus_ctrl_tx_clone);
|
||||
Self::time_update(
|
||||
item,
|
||||
playback_time,
|
||||
&duration_cache,
|
||||
&dbus_ctrl_tx_clone,
|
||||
);
|
||||
playback_time += 1;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
@ -205,25 +210,34 @@ impl SystemControlWrapper {
|
|||
|
||||
fn build_metadata(item: Item) -> Metadata {
|
||||
let file_uri = item.field("filename").and_then(|x| x.to_owned().to_str());
|
||||
let cover_art = item.field("cover")
|
||||
.and_then(|x| x.to_owned().to_str());
|
||||
let cover_art = item.field("cover").and_then(|x| x.to_owned().to_str());
|
||||
let cover_url = if let Some(art) = &cover_art {
|
||||
const DATA_START: usize = 23;
|
||||
const DATA_PREVIEW: usize = 32;
|
||||
const DATA_PREVIEW_OFFSET: usize = 128;
|
||||
let preview_slice_start = (DATA_START+DATA_PREVIEW_OFFSET).clamp(0, art.len()-2);
|
||||
let preview_slide_end = (DATA_START+DATA_PREVIEW+DATA_PREVIEW_OFFSET).clamp(preview_slice_start, art.len());
|
||||
let path = format!("{}/muss-cover-{}.jpg",
|
||||
std::env::var("HOME").map(|home| home + "/.cache").unwrap_or_else(|_| "/tmp".to_owned()),
|
||||
&art[preview_slice_start..preview_slide_end].replace("/", ""));
|
||||
let preview_slice_start = (DATA_START + DATA_PREVIEW_OFFSET).clamp(0, art.len() - 2);
|
||||
let preview_slide_end = (DATA_START + DATA_PREVIEW + DATA_PREVIEW_OFFSET)
|
||||
.clamp(preview_slice_start, art.len());
|
||||
let path = format!(
|
||||
"{}/muss-cover-{}.jpg",
|
||||
std::env::var("HOME")
|
||||
.map(|home| home + "/.cache")
|
||||
.unwrap_or_else(|_| "/tmp".to_owned()),
|
||||
&art[preview_slice_start..preview_slide_end].replace("/", "")
|
||||
);
|
||||
//let pathbuf = std::path::PathBuf::from(&path);
|
||||
/*if !pathbuf.exists() {
|
||||
|
||||
} else {
|
||||
Some(path)
|
||||
}*/
|
||||
base64::decode(&art[DATA_START..]).ok()
|
||||
.and_then(|decoded| std::fs::File::create(&path).ok().map(|file| (decoded, file)))
|
||||
base64::decode(&art[DATA_START..])
|
||||
.ok()
|
||||
.and_then(|decoded| {
|
||||
std::fs::File::create(&path)
|
||||
.ok()
|
||||
.map(|file| (decoded, file))
|
||||
})
|
||||
.and_then(|(decoded, mut file)| file.write(&decoded).ok())
|
||||
.map(|_| path)
|
||||
} else {
|
||||
|
@ -290,12 +304,15 @@ impl SystemControlWrapper {
|
|||
fn time(item: Item, duration: std::time::Duration, dbus_ctrl: &Sender<DbusControl>) {
|
||||
let mut meta = Self::build_metadata(item);
|
||||
meta.length = Some(duration.as_secs_f64().round() as i64 * 1_000_000);
|
||||
dbus_ctrl
|
||||
.send(DbusControl::SetMetadata(meta))
|
||||
.unwrap_or(());
|
||||
dbus_ctrl.send(DbusControl::SetMetadata(meta)).unwrap_or(());
|
||||
}
|
||||
|
||||
fn time_update(_item: Item, new_time: i64, duration: &Option<std::time::Duration>, dbus_ctrl: &Sender<DbusControl>) {
|
||||
fn time_update(
|
||||
_item: Item,
|
||||
new_time: i64,
|
||||
duration: &Option<std::time::Duration>,
|
||||
dbus_ctrl: &Sender<DbusControl>,
|
||||
) {
|
||||
//println!("Position update tick");
|
||||
if duration.is_some() {
|
||||
/*let mut meta = Self::build_metadata(item);
|
||||
|
|
|
@ -133,7 +133,14 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> Player<I> {
|
|||
Ok(enqueued)
|
||||
}
|
||||
|
||||
pub fn enqueue_modified(&mut self, count: usize, modify: &dyn Fn(Decoder<io::BufReader<fs::File>>, Item) -> Box<dyn Source<Item=i16> + Send>) -> Result<Vec<Item>, PlayerError> {
|
||||
pub fn enqueue_modified(
|
||||
&mut self,
|
||||
count: usize,
|
||||
modify: &dyn Fn(
|
||||
Decoder<io::BufReader<fs::File>>,
|
||||
Item,
|
||||
) -> Box<dyn Source<Item = i16> + Send>,
|
||||
) -> Result<Vec<Item>, PlayerError> {
|
||||
let mut items_left = count;
|
||||
let mut enqueued = Vec::with_capacity(count);
|
||||
if items_left == 0 {
|
||||
|
@ -245,8 +252,8 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> Player<I> {
|
|||
match uri.scheme() {
|
||||
Some(s) => match &s.to_lowercase() as &str {
|
||||
"file:" => {
|
||||
let file =
|
||||
fs::File::open(uri.without_scheme()).map_err(|e| PlayerError::from_file_err_playback(e, uri.path()))?;
|
||||
let file = fs::File::open(uri.without_scheme())
|
||||
.map_err(|e| PlayerError::from_file_err_playback(e, uri.path()))?;
|
||||
let stream = io::BufReader::new(file);
|
||||
let source = Decoder::new(stream).map_err(PlayerError::from_err_playback)?;
|
||||
self.sink.append(source);
|
||||
|
@ -284,13 +291,17 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> Player<I> {
|
|||
}
|
||||
}
|
||||
|
||||
fn append_source_modified(&mut self, filename: &str, modify: &dyn Fn(Decoder<io::BufReader<fs::File>>) -> Box<dyn Source<Item=i16> + Send>) -> Result<(), PlayerError> {
|
||||
fn append_source_modified(
|
||||
&mut self,
|
||||
filename: &str,
|
||||
modify: &dyn Fn(Decoder<io::BufReader<fs::File>>) -> Box<dyn Source<Item = i16> + Send>,
|
||||
) -> Result<(), PlayerError> {
|
||||
let uri = Uri::new(filename);
|
||||
match uri.scheme() {
|
||||
Some(s) => match &s.to_lowercase() as &str {
|
||||
"file:" => {
|
||||
let file =
|
||||
fs::File::open(uri.without_scheme()).map_err(|e| PlayerError::from_file_err_playback(e, uri.path()))?;
|
||||
let file = fs::File::open(uri.without_scheme())
|
||||
.map_err(|e| PlayerError::from_file_err_playback(e, uri.path()))?;
|
||||
let stream = io::BufReader::new(file);
|
||||
let source = Decoder::new(stream).map_err(PlayerError::from_err_playback)?;
|
||||
self.sink.append(modify(source));
|
||||
|
|
|
@ -38,29 +38,35 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> PlayerServer
|
|||
}
|
||||
}
|
||||
|
||||
fn modify(&self) -> impl Fn(rodio::Decoder<std::io::BufReader<std::fs::File>>, Item) -> Box<dyn Source<Item=i16> + Send> {
|
||||
fn modify(
|
||||
&self,
|
||||
) -> impl Fn(
|
||||
rodio::Decoder<std::io::BufReader<std::fs::File>>,
|
||||
Item,
|
||||
) -> Box<dyn Source<Item = i16> + Send> {
|
||||
let event = std::sync::Arc::new(std::sync::Mutex::new(self.playback.clone()));
|
||||
move |source_in, item| {
|
||||
let event2 = event.clone();
|
||||
if let Some(duration) = source_in.total_duration() {
|
||||
event.lock().map(|event|
|
||||
event.send(
|
||||
PlaybackAction::Time(item.clone(), duration)
|
||||
).unwrap_or(())
|
||||
).unwrap_or(());
|
||||
Box::new(
|
||||
source_in.periodic_access(
|
||||
std::time::Duration::from_secs(1),
|
||||
move |_|
|
||||
{
|
||||
//println!("Debug tick");
|
||||
event2.lock()
|
||||
.map(|x|
|
||||
x.send(PlaybackAction::UpdateTick(item.clone())).unwrap_or(())
|
||||
)
|
||||
event
|
||||
.lock()
|
||||
.map(|event| {
|
||||
event
|
||||
.send(PlaybackAction::Time(item.clone(), duration))
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(());
|
||||
}
|
||||
)
|
||||
Box::new(
|
||||
source_in.periodic_access(std::time::Duration::from_secs(1), move |_| {
|
||||
//println!("Debug tick");
|
||||
event2
|
||||
.lock()
|
||||
.map(|x| {
|
||||
x.send(PlaybackAction::UpdateTick(item.clone()))
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(());
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
// manually calculate length
|
||||
|
@ -73,25 +79,28 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> PlayerServer
|
|||
let sample_rate = source_in2.sample_rate();
|
||||
let channels = source_in2.channels() as u32;
|
||||
let sample_count = source_in2.count() as f64;
|
||||
let duration = std::time::Duration::from_secs_f64(sample_count / ((sample_rate * channels) as f64));
|
||||
event3.lock().map(|event|
|
||||
event.send(
|
||||
PlaybackAction::Time(item2.clone(), duration)
|
||||
).unwrap_or(())
|
||||
).unwrap_or(());
|
||||
let duration = std::time::Duration::from_secs_f64(
|
||||
sample_count / ((sample_rate * channels) as f64),
|
||||
);
|
||||
event3
|
||||
.lock()
|
||||
.map(|event| {
|
||||
event
|
||||
.send(PlaybackAction::Time(item2.clone(), duration))
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(());
|
||||
});
|
||||
Box::new(
|
||||
source_in.periodic_access(
|
||||
std::time::Duration::from_secs(1),
|
||||
move |_|
|
||||
{
|
||||
event2.lock()
|
||||
.map(|x|
|
||||
x.send(PlaybackAction::UpdateTick(item.clone())).unwrap_or(())
|
||||
)
|
||||
source_in.periodic_access(std::time::Duration::from_secs(1), move |_| {
|
||||
event2
|
||||
.lock()
|
||||
.map(|x| {
|
||||
x.send(PlaybackAction::UpdateTick(item.clone()))
|
||||
.unwrap_or(())
|
||||
})
|
||||
.unwrap_or(());
|
||||
}
|
||||
)
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
10
src/repl.rs
10
src/repl.rs
|
@ -120,17 +120,11 @@ fn pretty_print_item(item: &Item, terminal: &mut Term, args: &CliArgs, verbose:
|
|||
terminal,
|
||||
" {}: `{}[...]`",
|
||||
field,
|
||||
&field_str[..max_len-5]
|
||||
&field_str[..max_len - 5]
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
} else {
|
||||
writeln!(
|
||||
terminal,
|
||||
" {}: `{}`",
|
||||
field,
|
||||
field_str
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
writeln!(terminal, " {}: `{}`", field, field_str).expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue