cargo fmt

This commit is contained in:
NGnius (Graham) 2023-07-14 17:52:17 -04:00
parent 1198f58862
commit c3d605420f
47 changed files with 738 additions and 451 deletions

45
Cargo.lock generated
View file

@ -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",

View file

@ -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

View file

@ -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))

View file

@ -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 {

View file

@ -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))
}
}

View file

@ -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,
}
}

View file

@ -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),
})
}
}

View file

@ -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)?;

View file

@ -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,
};
}

View file

@ -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(

View file

@ -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
}
}
}

View file

@ -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;

View file

@ -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(

View file

@ -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
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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(

View file

@ -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;
}

View file

@ -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};

View file

@ -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(

View file

@ -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),
})
}
}

View file

@ -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(

View file

@ -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 {

View file

@ -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};

View file

@ -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};

View file

@ -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 {

View file

@ -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};

View file

@ -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()
}
}

View file

@ -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()
}
}

View file

@ -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,
)

View file

@ -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 {

View file

@ -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> {

View file

@ -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()
)))
}
}
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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)),
}
}

View file

@ -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))

View file

@ -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()
}
}

View file

@ -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,

View file

@ -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" }

View file

@ -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 {

View file

@ -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);

View file

@ -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));

View file

@ -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(());
}
)
}),
)
}
}

View file

@ -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);
}
}
}