cargo fmt
This commit is contained in:
parent
21f7149660
commit
175d304f1b
52 changed files with 495 additions and 434 deletions
|
@ -51,5 +51,8 @@ fn faye_benchmark(c: &mut Criterion) {
|
|||
});
|
||||
}
|
||||
|
||||
criterion_group!(parse_benches, /*interpretor_benchmark,*/ faye_benchmark);
|
||||
criterion_group!(
|
||||
parse_benches,
|
||||
/*interpretor_benchmark,*/ faye_benchmark
|
||||
);
|
||||
criterion_main!(parse_benches);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
use super::processing::advanced::{DefaultAnalyzer, MusicAnalyzer};
|
||||
use super::processing::database::{DatabaseQuerier, SQLiteExecutor};
|
||||
#[cfg(feature = "mpd")]
|
||||
use super::processing::database::{MpdQuerier, MpdExecutor};
|
||||
use super::processing::database::{MpdExecutor, MpdQuerier};
|
||||
use super::processing::general::{
|
||||
FilesystemExecutor, FilesystemQuerier, OpStorage, VariableStorer,
|
||||
};
|
||||
|
|
|
@ -7,10 +7,7 @@ use super::{Interpreter, InterpreterItem};
|
|||
pub struct Debugger<'a, T, F>
|
||||
where
|
||||
T: TokenReader,
|
||||
F: Fn(
|
||||
&mut Interpreter<'a, T>,
|
||||
Option<InterpreterItem>,
|
||||
) -> Option<InterpreterItem>,
|
||||
F: Fn(&mut Interpreter<'a, T>, Option<InterpreterItem>) -> Option<InterpreterItem>,
|
||||
{
|
||||
interpreter: Interpreter<'a, T>,
|
||||
transmuter: F,
|
||||
|
@ -19,16 +16,10 @@ where
|
|||
impl<'a, T, F> Debugger<'a, T, F>
|
||||
where
|
||||
T: TokenReader,
|
||||
F: Fn(
|
||||
&mut Interpreter<'a, T>,
|
||||
Option<InterpreterItem>,
|
||||
) -> Option<InterpreterItem>,
|
||||
F: Fn(&mut Interpreter<'a, T>, Option<InterpreterItem>) -> Option<InterpreterItem>,
|
||||
{
|
||||
/// Create a new instance of Debugger using the provided interpreter and callback.
|
||||
pub fn new(
|
||||
faye: Interpreter<'a, T>,
|
||||
item_handler: F,
|
||||
) -> Self {
|
||||
pub fn new(faye: Interpreter<'a, T>, item_handler: F) -> Self {
|
||||
Self {
|
||||
interpreter: faye,
|
||||
transmuter: item_handler,
|
||||
|
@ -39,10 +30,7 @@ where
|
|||
impl<'a, T, F> Iterator for Debugger<'a, T, F>
|
||||
where
|
||||
T: TokenReader,
|
||||
F: Fn(
|
||||
&mut Interpreter<'a, T>,
|
||||
Option<InterpreterItem>,
|
||||
) -> Option<InterpreterItem>,
|
||||
F: Fn(&mut Interpreter<'a, T>, Option<InterpreterItem>) -> Option<InterpreterItem>,
|
||||
{
|
||||
type Item = InterpreterItem;
|
||||
|
||||
|
|
|
@ -61,7 +61,13 @@ impl<'a, R: Read> Interpreter<'a, Tokenizer<R>> {
|
|||
Self::with_standard_vocab(tokenizer)
|
||||
}
|
||||
|
||||
pub fn with_stream_and_callback(stream: R, callback: &'a dyn Fn(&mut Interpreter<'a, Tokenizer<R>>, InterpreterEvent) -> Result<(), InterpreterError>) -> Self {
|
||||
pub fn with_stream_and_callback(
|
||||
stream: R,
|
||||
callback: &'a dyn Fn(
|
||||
&mut Interpreter<'a, Tokenizer<R>>,
|
||||
InterpreterEvent,
|
||||
) -> Result<(), InterpreterError>,
|
||||
) -> Self {
|
||||
let tokenizer = Tokenizer::new(stream);
|
||||
let vocab = LanguageDictionary::standard();
|
||||
Self::with(vocab, tokenizer, callback)
|
||||
|
@ -89,7 +95,10 @@ where
|
|||
pub fn with(
|
||||
vocab: LanguageDictionary,
|
||||
token_reader: T,
|
||||
callback: &'a dyn Fn(&mut Interpreter<'a, T>, InterpreterEvent) -> Result<(), InterpreterError>,
|
||||
callback: &'a dyn Fn(
|
||||
&mut Interpreter<'a, T>,
|
||||
InterpreterEvent,
|
||||
) -> Result<(), InterpreterError>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tokenizer: token_reader,
|
||||
|
@ -190,7 +199,10 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn error_with_ctx<T: std::convert::Into<InterpreterError>>(error: T, line: usize) -> InterpreterError {
|
||||
fn error_with_ctx<T: std::convert::Into<InterpreterError>>(
|
||||
error: T,
|
||||
line: usize,
|
||||
) -> InterpreterError {
|
||||
let mut err = error.into();
|
||||
err.set_line(line);
|
||||
err
|
||||
|
|
|
@ -237,15 +237,14 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
|
|||
// handle other filters
|
||||
// make fake inner item
|
||||
let single_op = SingleItem::new_ok(item.clone());
|
||||
match ctx.variables.declare(
|
||||
INNER_VARIABLE_NAME,
|
||||
Type::Op(Box::new(single_op)),
|
||||
) {
|
||||
match ctx
|
||||
.variables
|
||||
.declare(INNER_VARIABLE_NAME, Type::Op(Box::new(single_op)))
|
||||
{
|
||||
Ok(x) => x,
|
||||
Err(e) => {
|
||||
//self.context = Some(op.escape());
|
||||
maybe_result =
|
||||
Some(Err(e.with(RuntimeOp(fake))));
|
||||
maybe_result = Some(Err(e.with(RuntimeOp(fake))));
|
||||
self.context = Some(ctx);
|
||||
break;
|
||||
}
|
||||
|
@ -268,9 +267,8 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
|
|||
Ok(_) => {}
|
||||
Err(e) => match maybe_result {
|
||||
Some(Ok(_)) => {
|
||||
maybe_result = Some(Err(
|
||||
e.with(RuntimeOp(fake))
|
||||
))
|
||||
maybe_result =
|
||||
Some(Err(e.with(RuntimeOp(fake))))
|
||||
}
|
||||
Some(Err(e2)) => maybe_result = Some(Err(e2)), // already failing, do not replace error,
|
||||
None => {} // impossible
|
||||
|
@ -332,8 +330,8 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
|
|||
}
|
||||
Err(e) => {
|
||||
self.is_failing = true; // this is unrecoverable and reproducible, so it shouldn't be tried again (to prevent error spam)
|
||||
return Some(Err(e.with(RuntimeOp(fake))))
|
||||
},
|
||||
return Some(Err(e.with(RuntimeOp(fake))));
|
||||
}
|
||||
};
|
||||
let mut maybe_result = None;
|
||||
let ctx = self.context.take().unwrap();
|
||||
|
@ -457,17 +455,12 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct FilterStatementFactory<
|
||||
P: FilterPredicate + 'static,
|
||||
F: FilterFactory<P> + 'static,
|
||||
> {
|
||||
pub struct FilterStatementFactory<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> {
|
||||
filter_factory: F,
|
||||
idc: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static>
|
||||
FilterStatementFactory<P, F>
|
||||
{
|
||||
impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> FilterStatementFactory<P, F> {
|
||||
pub fn new(factory: F) -> Self {
|
||||
Self {
|
||||
filter_factory: factory,
|
||||
|
|
|
@ -354,10 +354,7 @@ fn declare_or_replace_item(
|
|||
Ok(old_item)
|
||||
}
|
||||
|
||||
fn remove_or_replace_item(
|
||||
old_item: Option<Type>,
|
||||
ctx: &mut Context,
|
||||
) -> Result<(), RuntimeMsg> {
|
||||
fn remove_or_replace_item(old_item: Option<Type>, ctx: &mut Context) -> Result<(), RuntimeMsg> {
|
||||
ctx.variables.remove(ITEM_VARIABLE_NAME)?;
|
||||
if let Some(old_item) = old_item {
|
||||
ctx.variables.declare(ITEM_VARIABLE_NAME, old_item)?;
|
||||
|
|
|
@ -189,10 +189,7 @@ impl Iterator for ItemBlockStatement {
|
|||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: format!(
|
||||
"Expected `item` like Type::Item(Item[...]), got {}",
|
||||
x
|
||||
),
|
||||
msg: format!("Expected `item` like Type::Item(Item[...]), got {}", x),
|
||||
}))
|
||||
}
|
||||
None => {}
|
||||
|
@ -214,10 +211,7 @@ pub struct ItemBlockFactory {
|
|||
}
|
||||
|
||||
impl ItemBlockFactory {
|
||||
pub fn push<
|
||||
T: ItemOpFactory<Y> + 'static,
|
||||
Y: Deref<Target = dyn ItemOp> + ItemOp + 'static,
|
||||
>(
|
||||
pub fn push<T: ItemOpFactory<Y> + 'static, Y: Deref<Target = dyn ItemOp> + ItemOp + 'static>(
|
||||
mut self,
|
||||
factory: T,
|
||||
) -> Self {
|
||||
|
@ -314,10 +308,7 @@ fn replace_item_var(ctx: &mut Context, item: Type) -> Option<Type> {
|
|||
old_var
|
||||
}
|
||||
|
||||
fn restore_item_var(
|
||||
ctx: &mut Context,
|
||||
old_var: Option<Type>,
|
||||
) -> Result<Option<Type>, RuntimeMsg> {
|
||||
fn restore_item_var(ctx: &mut Context, old_var: Option<Type>) -> Result<Option<Type>, RuntimeMsg> {
|
||||
let new_var = if ctx.variables.exists(ITEM_VARIABLE_NAME) {
|
||||
Some(ctx.variables.remove(ITEM_VARIABLE_NAME)?)
|
||||
} else {
|
||||
|
|
|
@ -20,9 +20,7 @@ pub(crate) mod utility;
|
|||
pub use dictionary::LanguageDictionary;
|
||||
pub(crate) use error::LanguageError;
|
||||
pub use error::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
|
||||
pub use filter::{
|
||||
FilterFactory, FilterPredicate, FilterStatement, FilterStatementFactory,
|
||||
};
|
||||
pub use filter::{FilterFactory, FilterPredicate, FilterStatement, FilterStatementFactory};
|
||||
pub use filter_replace::FilterReplaceStatement;
|
||||
pub use function::{FunctionFactory, FunctionStatementFactory};
|
||||
pub use iter_block::{ItemBlockFactory, ItemOp, ItemOpFactory};
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use super::TypePrimitive;
|
||||
use super::SyntaxError;
|
||||
use super::TypePrimitive;
|
||||
use crate::tokens::Token;
|
||||
|
||||
pub fn assert_token<T, F: FnOnce(Token) -> Option<T>>(
|
||||
|
@ -29,10 +29,7 @@ pub fn assert_token<T, F: FnOnce(Token) -> Option<T>>(
|
|||
}
|
||||
}
|
||||
|
||||
pub fn assert_token_raw(
|
||||
token: Token,
|
||||
tokens: &mut VecDeque<Token>,
|
||||
) -> Result<Token, SyntaxError> {
|
||||
pub fn assert_token_raw(token: Token, tokens: &mut VecDeque<Token>) -> Result<Token, SyntaxError> {
|
||||
let result = match tokens.pop_front() {
|
||||
Some(x) => Ok(x),
|
||||
None => Err(SyntaxError {
|
||||
|
|
|
@ -5,8 +5,8 @@ use std::iter::Iterator;
|
|||
use crate::tokens::Token;
|
||||
use crate::Context;
|
||||
|
||||
use crate::lang::{Lookup, LanguageDictionary, PseudoOp};
|
||||
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op};
|
||||
use crate::lang::{LanguageDictionary, Lookup, PseudoOp};
|
||||
use crate::lang::{RuntimeError, RuntimeOp, SyntaxError};
|
||||
use crate::processing::general::Type;
|
||||
use crate::Item;
|
||||
|
|
|
@ -47,11 +47,7 @@ impl Display for FieldFilter {
|
|||
}
|
||||
|
||||
impl FilterPredicate for FieldFilter {
|
||||
fn matches(
|
||||
&mut self,
|
||||
music_item_lut: &Item,
|
||||
ctx: &mut Context,
|
||||
) -> Result<bool, RuntimeMsg> {
|
||||
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
|
||||
let variable = match &self.val {
|
||||
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
|
||||
Type::Primitive(t) => Ok(t),
|
||||
|
|
|
@ -40,11 +40,7 @@ impl Display for FieldLikeFilter {
|
|||
}
|
||||
|
||||
impl FilterPredicate for FieldLikeFilter {
|
||||
fn matches(
|
||||
&mut self,
|
||||
music_item_lut: &Item,
|
||||
ctx: &mut Context,
|
||||
) -> Result<bool, RuntimeMsg> {
|
||||
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
|
||||
let variable = match &self.val {
|
||||
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
|
||||
Type::Primitive(TypePrimitive::String(s)) => Ok(s),
|
||||
|
@ -122,13 +118,11 @@ impl FilterFactory<FieldLikeFilter> for FieldLikeFilterFactory {
|
|||
};
|
||||
let name = assert_token(
|
||||
|t| match t {
|
||||
Token::Name(s) => {
|
||||
match &s as _ {
|
||||
Token::Name(s) => match &s as _ {
|
||||
"unlike" | "like" => Some(s),
|
||||
_ => None,
|
||||
}
|
||||
},
|
||||
_ => None
|
||||
_ => None,
|
||||
},
|
||||
Token::Literal("like|unlike".into()),
|
||||
tokens,
|
||||
|
|
|
@ -33,11 +33,7 @@ impl Display for FieldRegexFilter {
|
|||
}
|
||||
|
||||
impl FilterPredicate for FieldRegexFilter {
|
||||
fn matches(
|
||||
&mut self,
|
||||
music_item_lut: &Item,
|
||||
ctx: &mut Context,
|
||||
) -> Result<bool, RuntimeMsg> {
|
||||
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
|
||||
let variable = match &self.val {
|
||||
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
|
||||
Type::Primitive(TypePrimitive::String(s)) => Ok(s),
|
||||
|
|
|
@ -2,8 +2,8 @@ use std::collections::VecDeque;
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{utility::assert_token_raw, RuntimeMsg, SyntaxError};
|
||||
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError, utility::assert_token_raw};
|
||||
use crate::tokens::Token;
|
||||
use crate::Context;
|
||||
use crate::Item;
|
||||
|
@ -57,7 +57,8 @@ impl FilterFactory<NonEmptyFilter> for NonEmptyFilterFactory {
|
|||
}
|
||||
}
|
||||
|
||||
pub type NonEmptyFilterStatementFactory = FilterStatementFactory<NonEmptyFilter, NonEmptyFilterFactory>;
|
||||
pub type NonEmptyFilterStatementFactory =
|
||||
FilterStatementFactory<NonEmptyFilter, NonEmptyFilterFactory>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn nonempty_filter() -> NonEmptyFilterStatementFactory {
|
||||
|
|
|
@ -151,8 +151,7 @@ pub fn unique_field_filter() -> UniqueFieldFilterStatementFactory {
|
|||
UniqueFieldFilterStatementFactory::new(UniqueFilterFactory)
|
||||
}
|
||||
|
||||
pub type UniqueFilterStatementFactory =
|
||||
FilterStatementFactory<UniqueFilter, UniqueFilterFactory>;
|
||||
pub type UniqueFilterStatementFactory = FilterStatementFactory<UniqueFilter, UniqueFilterFactory>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn unique_filter() -> UniqueFilterStatementFactory {
|
||||
|
|
|
@ -35,9 +35,7 @@ impl ItemOp for AddItemOp {
|
|||
if let Type::Primitive(lhs) = &lhs {
|
||||
let rhs = self.rhs.execute(context)?;
|
||||
if let Type::Primitive(rhs) = &rhs {
|
||||
Ok(Type::Primitive(
|
||||
lhs.try_add(rhs).map_err(RuntimeMsg)?,
|
||||
))
|
||||
Ok(Type::Primitive(lhs.try_add(rhs).map_err(RuntimeMsg)?))
|
||||
} else {
|
||||
Err(RuntimeMsg(format!(
|
||||
"Cannot add right-hand side `{}` ({}): not primitive type",
|
||||
|
|
|
@ -32,9 +32,7 @@ impl ItemOp for NegateItemOp {
|
|||
fn execute(&self, context: &mut Context) -> Result<Type, RuntimeMsg> {
|
||||
let rhs = self.rhs.execute(context)?;
|
||||
if let Type::Primitive(rhs) = &rhs {
|
||||
Ok(Type::Primitive(
|
||||
rhs.try_negate().map_err(RuntimeMsg)?,
|
||||
))
|
||||
Ok(Type::Primitive(rhs.try_negate().map_err(RuntimeMsg)?))
|
||||
} else {
|
||||
Err(RuntimeMsg(format!(
|
||||
"Cannot negate `{}` ({}): not primitive type",
|
||||
|
|
|
@ -32,9 +32,7 @@ impl ItemOp for NotItemOp {
|
|||
fn execute(&self, context: &mut Context) -> Result<Type, RuntimeMsg> {
|
||||
let rhs = self.rhs.execute(context)?;
|
||||
if let Type::Primitive(rhs) = &rhs {
|
||||
Ok(Type::Primitive(
|
||||
rhs.try_not().map_err(RuntimeMsg)?,
|
||||
))
|
||||
Ok(Type::Primitive(rhs.try_not().map_err(RuntimeMsg)?))
|
||||
} else {
|
||||
Err(RuntimeMsg(format!(
|
||||
"Cannot apply logical NOT to `{}` ({}): not primitive type",
|
||||
|
|
|
@ -35,9 +35,7 @@ impl ItemOp for SubtractItemOp {
|
|||
if let Type::Primitive(lhs) = &lhs {
|
||||
let rhs = self.rhs.execute(context)?;
|
||||
if let Type::Primitive(rhs) = &rhs {
|
||||
Ok(Type::Primitive(
|
||||
lhs.try_subtract(rhs).map_err(RuntimeMsg)?,
|
||||
))
|
||||
Ok(Type::Primitive(lhs.try_subtract(rhs).map_err(RuntimeMsg)?))
|
||||
} else {
|
||||
Err(RuntimeMsg(format!(
|
||||
"Cannot subtract right-hand side `{}` ({}): not primitive type",
|
||||
|
|
|
@ -6,11 +6,11 @@ use std::net::SocketAddr;
|
|||
use crate::tokens::Token;
|
||||
use crate::Context;
|
||||
|
||||
use crate::lang::{LanguageDictionary, repeated_tokens, Lookup};
|
||||
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp};
|
||||
use crate::lang::{RuntimeError, SyntaxError, RuntimeOp};
|
||||
use crate::lang::utility::{assert_token, assert_token_raw};
|
||||
use crate::lang::TypePrimitive;
|
||||
use crate::lang::{repeated_tokens, LanguageDictionary, Lookup};
|
||||
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp};
|
||||
use crate::lang::{RuntimeError, RuntimeOp, SyntaxError};
|
||||
use crate::processing::general::Type;
|
||||
use crate::Item;
|
||||
|
||||
|
@ -53,68 +53,88 @@ impl Iterator for MpdQueryStatement {
|
|||
// build address
|
||||
let addr_str = match self.addr.get(self.context.as_mut().unwrap()) {
|
||||
Ok(Type::Primitive(a)) => a.as_str(),
|
||||
Ok(x) => return Some(Err(
|
||||
RuntimeError {
|
||||
Ok(x) => {
|
||||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
msg: format!("Cannot use non-primitive `{}` as IP address", x),
|
||||
op: PseudoOp::from_printable(self),
|
||||
}))
|
||||
}
|
||||
)),
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
|
||||
};
|
||||
#[cfg(not(feature = "ergonomics"))]
|
||||
let addr: SocketAddr = match addr_str.parse() {
|
||||
Ok(a) => a,
|
||||
Err(e) => return Some(Err(RuntimeError {
|
||||
Err(e) => {
|
||||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: format!("Cannot convert `{}` to IP Address: {}", addr_str, e),
|
||||
}))
|
||||
}
|
||||
};
|
||||
#[cfg(feature = "ergonomics")]
|
||||
let addr: SocketAddr = if addr_str.starts_with("localhost:") {
|
||||
let port_str = addr_str.replace("localhost:", "");
|
||||
let port = match port_str.parse::<u16>() {
|
||||
Ok(p) => p,
|
||||
Err(e) => return Some(Err(RuntimeError {
|
||||
Err(e) => {
|
||||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: format!("Cannot convert `{}` to IP port: {}", port_str, e),
|
||||
}))
|
||||
}
|
||||
};
|
||||
SocketAddr::V4(std::net::SocketAddrV4::new(std::net::Ipv4Addr::LOCALHOST, port))
|
||||
SocketAddr::V4(std::net::SocketAddrV4::new(
|
||||
std::net::Ipv4Addr::LOCALHOST,
|
||||
port,
|
||||
))
|
||||
} else if addr_str == "default" {
|
||||
SocketAddr::V4(std::net::SocketAddrV4::new(std::net::Ipv4Addr::LOCALHOST, 6600))
|
||||
SocketAddr::V4(std::net::SocketAddrV4::new(
|
||||
std::net::Ipv4Addr::LOCALHOST,
|
||||
6600,
|
||||
))
|
||||
} else {
|
||||
match addr_str.parse() {
|
||||
Ok(a) => a,
|
||||
Err(e) => return Some(Err(RuntimeError {
|
||||
Err(e) => {
|
||||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
op: PseudoOp::from_printable(self),
|
||||
msg: format!("Cannot convert `{}` to IP Address: {}", addr_str, e),
|
||||
}))
|
||||
}
|
||||
}
|
||||
};
|
||||
// build params
|
||||
let mut new_params = Vec::<(&str, String)>::with_capacity(self.params.len());
|
||||
for (term, value) in self.params.iter() {
|
||||
let static_val = match value.get(self.context.as_mut().unwrap()) {
|
||||
Ok(Type::Primitive(a)) => a.as_str(),
|
||||
Ok(x) => return Some(Err(
|
||||
RuntimeError {
|
||||
Ok(x) => {
|
||||
return Some(Err(RuntimeError {
|
||||
line: 0,
|
||||
msg: format!("Cannot use non-primitive `{}` MPS query value", x),
|
||||
op: PseudoOp::from_printable(self),
|
||||
}))
|
||||
}
|
||||
)),
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
|
||||
};
|
||||
new_params.push((term, static_val));
|
||||
}
|
||||
self.results = Some(match self.context.as_mut().unwrap().mpd_database.one_shot_search(addr, new_params) {
|
||||
self.results = Some(
|
||||
match self
|
||||
.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.mpd_database
|
||||
.one_shot_search(addr, new_params)
|
||||
{
|
||||
Ok(items) => items,
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))))
|
||||
});
|
||||
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
|
||||
},
|
||||
);
|
||||
}
|
||||
let results = self.results.as_mut().unwrap();
|
||||
results.pop_front().map(Ok)
|
||||
|
@ -169,7 +189,10 @@ impl FunctionFactory<MpdQueryStatement> for MpdQueryFunctionFactory {
|
|||
Ok(MpdQueryStatement {
|
||||
context: None,
|
||||
addr: addr_lookup,
|
||||
params: vec![("any".to_string(), Lookup::Static(Type::Primitive(TypePrimitive::String("".to_owned()))))],
|
||||
params: vec![(
|
||||
"any".to_string(),
|
||||
Lookup::Static(Type::Primitive(TypePrimitive::String("".to_owned()))),
|
||||
)],
|
||||
results: None,
|
||||
})
|
||||
} else {
|
||||
|
@ -182,13 +205,15 @@ impl FunctionFactory<MpdQueryStatement> for MpdQueryFunctionFactory {
|
|||
_ => None,
|
||||
},
|
||||
Token::Name("term".to_string()),
|
||||
tokens)?;
|
||||
tokens,
|
||||
)?;
|
||||
assert_token_raw(Token::Equals, tokens)?;
|
||||
let val = Lookup::parse(tokens)?;
|
||||
Ok(Some((term, val)))
|
||||
},
|
||||
Token::Comma
|
||||
).ingest_all(tokens)?;
|
||||
Token::Comma,
|
||||
)
|
||||
.ingest_all(tokens)?;
|
||||
Ok(MpdQueryStatement {
|
||||
context: None,
|
||||
addr: addr_lookup,
|
||||
|
@ -200,7 +225,8 @@ impl FunctionFactory<MpdQueryStatement> for MpdQueryFunctionFactory {
|
|||
}
|
||||
|
||||
#[cfg(feature = "mpd")]
|
||||
pub type MpdQueryStatementFactory = FunctionStatementFactory<MpdQueryStatement, MpdQueryFunctionFactory>;
|
||||
pub type MpdQueryStatementFactory =
|
||||
FunctionStatementFactory<MpdQueryStatement, MpdQueryFunctionFactory>;
|
||||
|
||||
#[cfg(feature = "mpd")]
|
||||
#[inline(always)]
|
||||
|
|
|
@ -8,9 +8,7 @@ use crate::Item;
|
|||
|
||||
use crate::lang::utility::{assert_token, assert_token_raw};
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{
|
||||
FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp,
|
||||
};
|
||||
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp};
|
||||
use crate::lang::{RuntimeError, SyntaxError};
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -285,8 +283,7 @@ fn next_comma(tokens: &VecDeque<Token>) -> usize {
|
|||
tokens.len()
|
||||
}
|
||||
|
||||
pub type RepeatStatementFactory =
|
||||
FunctionStatementFactory<RepeatStatement, RepeatFunctionFactory>;
|
||||
pub type RepeatStatementFactory = FunctionStatementFactory<RepeatStatement, RepeatFunctionFactory>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn repeat_function_factory() -> RepeatStatementFactory {
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
use crate::lang::utility::{assert_name, check_name};
|
||||
use crate::lang::SyntaxError;
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::lang::{IteratorItem, Op, Sorter, RuntimeMsg};
|
||||
use crate::lang::{IteratorItem, Op, RuntimeMsg, Sorter};
|
||||
use crate::lang::{LanguageDictionary, SortStatementFactory, SorterFactory};
|
||||
use crate::tokens::Token;
|
||||
#[cfg(feature = "advanced")]
|
||||
|
|
|
@ -8,7 +8,7 @@ use std::collections::HashMap;
|
|||
use crate::lang::utility::{assert_name, check_name};
|
||||
use crate::lang::SyntaxError;
|
||||
#[cfg(feature = "advanced")]
|
||||
use crate::lang::{IteratorItem, Op, Sorter, RuntimeMsg};
|
||||
use crate::lang::{IteratorItem, Op, RuntimeMsg, Sorter};
|
||||
use crate::lang::{LanguageDictionary, SortStatementFactory, SorterFactory};
|
||||
use crate::tokens::Token;
|
||||
#[cfg(feature = "advanced")]
|
||||
|
|
|
@ -2,8 +2,8 @@ use std::collections::VecDeque;
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
|
||||
use crate::lang::{IteratorItem, LanguageDictionary, Op};
|
||||
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
|
||||
use crate::tokens::Token;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
|
|
|
@ -4,8 +4,8 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
|
||||
use crate::lang::utility::assert_token;
|
||||
use crate::lang::{IteratorItem, LanguageDictionary, Op};
|
||||
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
|
||||
use crate::tokens::Token;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -5,8 +5,8 @@ use rand::{thread_rng, Rng};
|
|||
|
||||
use crate::lang::utility::{assert_name, check_name};
|
||||
use crate::lang::{IteratorItem, LanguageDictionary, Op};
|
||||
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
|
||||
use crate::lang::{RuntimeMsg, SyntaxError};
|
||||
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
|
||||
use crate::tokens::Token;
|
||||
|
||||
const RNG_LIMIT_BITMASK: usize = 0xffff; // bits to preserve in RNG
|
||||
|
@ -90,8 +90,7 @@ impl SorterFactory<ShuffleSorter> for ShuffleSorterFactory {
|
|||
}
|
||||
}
|
||||
|
||||
pub type ShuffleSorterStatementFactory =
|
||||
SortStatementFactory<ShuffleSorter, ShuffleSorterFactory>;
|
||||
pub type ShuffleSorterStatementFactory = SortStatementFactory<ShuffleSorter, ShuffleSorterFactory>;
|
||||
|
||||
#[inline(always)]
|
||||
pub fn shuffle_sort() -> ShuffleSorterStatementFactory {
|
||||
|
|
|
@ -4,8 +4,7 @@ use std::iter::Iterator;
|
|||
|
||||
use crate::lang::utility::assert_token;
|
||||
use crate::lang::{
|
||||
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op,
|
||||
PseudoOp,
|
||||
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op, PseudoOp,
|
||||
};
|
||||
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
|
||||
use crate::tokens::Token;
|
||||
|
|
|
@ -4,8 +4,7 @@ use std::iter::Iterator;
|
|||
|
||||
use crate::lang::utility::assert_token;
|
||||
use crate::lang::{
|
||||
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op,
|
||||
PseudoOp,
|
||||
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op, PseudoOp,
|
||||
};
|
||||
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
|
||||
use crate::tokens::Token;
|
||||
|
|
|
@ -7,9 +7,7 @@ use crate::Context;
|
|||
|
||||
use crate::lang::utility::{assert_token, assert_token_raw, assert_type, check_is_type};
|
||||
use crate::lang::LanguageDictionary;
|
||||
use crate::lang::{
|
||||
BoxedOpFactory, IteratorItem, Op, OpFactory, TypePrimitive, PseudoOp,
|
||||
};
|
||||
use crate::lang::{BoxedOpFactory, IteratorItem, Op, OpFactory, PseudoOp, TypePrimitive};
|
||||
use crate::lang::{RuntimeError, RuntimeOp, SyntaxError};
|
||||
use crate::processing::general::Type;
|
||||
|
||||
|
@ -75,15 +73,13 @@ impl Iterator for AssignStatement {
|
|||
Err(e) => return Some(Err(e)),
|
||||
};
|
||||
let result = if self.is_declaration {
|
||||
self
|
||||
.context
|
||||
self.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.declare(&self.variable_name, Type::Op(real))
|
||||
} else {
|
||||
self
|
||||
.context
|
||||
self.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
|
@ -107,15 +103,13 @@ impl Iterator for AssignStatement {
|
|||
} else {
|
||||
let assign_type = self.assign_type.clone().unwrap();
|
||||
let result = if self.is_declaration {
|
||||
self
|
||||
.context
|
||||
self.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
.declare(&self.variable_name, Type::Primitive(assign_type))
|
||||
} else {
|
||||
self
|
||||
.context
|
||||
self.context
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.variables
|
||||
|
|
|
@ -3,10 +3,7 @@ use std::path::Path;
|
|||
use super::Library;
|
||||
use crate::lang::db::*;
|
||||
|
||||
pub fn build_library_from_files<P: AsRef<Path>>(
|
||||
path: P,
|
||||
lib: &mut Library,
|
||||
) -> std::io::Result<()> {
|
||||
pub fn build_library_from_files<P: AsRef<Path>>(path: P, lib: &mut Library) -> std::io::Result<()> {
|
||||
//let mut result = Library::new();
|
||||
lib.read_path(path, 10)?;
|
||||
Ok(())
|
||||
|
|
|
@ -149,7 +149,9 @@ impl Tags {
|
|||
pub fn artist(&self, id: u64, genre_id: u64) -> DbArtistItem {
|
||||
DbArtistItem {
|
||||
artist_id: id,
|
||||
name: self.artist_name().unwrap_or_else(|| "Unknown Artist".into()),
|
||||
name: self
|
||||
.artist_name()
|
||||
.unwrap_or_else(|| "Unknown Artist".into()),
|
||||
genre: genre_id,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
|
|||
|
||||
use regex::Regex;
|
||||
|
||||
use crate::lang::{TypePrimitive, RuntimeMsg};
|
||||
use crate::lang::{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))$";
|
||||
|
@ -41,7 +41,8 @@ impl Iterator for SortedReadDir {
|
|||
}
|
||||
}
|
||||
self.dir_iter_complete = true;
|
||||
self.cache.sort_by_key(|b| std::cmp::Reverse(b.path().to_string_lossy().to_lowercase()));
|
||||
self.cache
|
||||
.sort_by_key(|b| std::cmp::Reverse(b.path().to_string_lossy().to_lowercase()));
|
||||
/*self.cache.sort_by(
|
||||
|a, b| b.path().to_string_lossy().to_lowercase().cmp(
|
||||
&a.path().to_string_lossy().to_lowercase())
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
mod filesystem;
|
||||
#[cfg(feature = "advanced")]
|
||||
mod music_analysis;
|
||||
#[cfg(feature = "mpd")]
|
||||
mod mpd;
|
||||
#[cfg(feature = "advanced")]
|
||||
mod music_analysis;
|
||||
mod sql;
|
||||
mod variables;
|
||||
|
||||
//pub type OpGetter = dyn FnMut() -> crate::lang::PseudoOp;
|
||||
|
||||
pub mod database {
|
||||
pub use super::sql::{DatabaseQuerier, SQLiteExecutor, QueryResult};
|
||||
#[cfg(feature = "mpd")]
|
||||
pub use super::mpd::{MpdQuerier, MpdExecutor};
|
||||
pub use super::mpd::{MpdExecutor, MpdQuerier};
|
||||
pub use super::sql::{DatabaseQuerier, QueryResult, SQLiteExecutor};
|
||||
}
|
||||
|
||||
pub mod general {
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use core::fmt::Debug;
|
||||
use std::collections::VecDeque;
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
use std::iter::Iterator;
|
||||
use std::net::{SocketAddr, TcpStream};
|
||||
|
||||
use mpd::Client;
|
||||
use mpd::{Query, Term, Song};
|
||||
use mpd::{Query, Song, Term};
|
||||
|
||||
use crate::lang::RuntimeMsg;
|
||||
use crate::Item;
|
||||
use crate::lang::TypePrimitive;
|
||||
use crate::Item;
|
||||
|
||||
/// Music Player Daemon interface for interacting with it's database
|
||||
pub trait MpdQuerier: Debug {
|
||||
|
@ -16,7 +16,11 @@ pub trait MpdQuerier: Debug {
|
|||
|
||||
fn search(&mut self, params: Vec<(&str, String)>) -> Result<VecDeque<Item>, RuntimeMsg>;
|
||||
|
||||
fn one_shot_search(&self, addr: SocketAddr, params: Vec<(&str, String)>) -> Result<VecDeque<Item>, RuntimeMsg>;
|
||||
fn one_shot_search(
|
||||
&self,
|
||||
addr: SocketAddr,
|
||||
params: Vec<(&str, String)>,
|
||||
) -> Result<VecDeque<Item>, RuntimeMsg>;
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
|
@ -26,7 +30,10 @@ pub struct MpdExecutor {
|
|||
|
||||
impl MpdQuerier for MpdExecutor {
|
||||
fn connect(&mut self, addr: SocketAddr) -> Result<(), RuntimeMsg> {
|
||||
self.connection = Some(Client::connect(addr).map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?);
|
||||
self.connection = Some(
|
||||
Client::connect(addr)
|
||||
.map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -40,19 +47,31 @@ impl MpdQuerier for MpdExecutor {
|
|||
for (term, value) in params {
|
||||
query_mut = query_mut.and(str_to_term(term), value);
|
||||
}
|
||||
let songs = self.connection.as_mut().unwrap().search(query_mut, None).map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
|
||||
let songs = self
|
||||
.connection
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.search(query_mut, None)
|
||||
.map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
|
||||
Ok(songs.into_iter().map(song_to_item).collect())
|
||||
}
|
||||
|
||||
fn one_shot_search(&self, addr: SocketAddr, params: Vec<(&str, String)>) -> Result<VecDeque<Item>, RuntimeMsg> {
|
||||
let mut connection = Client::connect(addr).map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?;
|
||||
fn one_shot_search(
|
||||
&self,
|
||||
addr: SocketAddr,
|
||||
params: Vec<(&str, String)>,
|
||||
) -> Result<VecDeque<Item>, RuntimeMsg> {
|
||||
let mut connection = Client::connect(addr)
|
||||
.map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?;
|
||||
//let music_dir = connection.music_directory().map_err(|e| RuntimeMsg(format!("MPD command error: {}", e)))?;
|
||||
let mut query = Query::new();
|
||||
let mut query_mut = &mut query;
|
||||
for (term, value) in params {
|
||||
query_mut = query_mut.and(str_to_term(term), value);
|
||||
}
|
||||
let songs = connection.search(query_mut, None).map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
|
||||
let songs = connection
|
||||
.search(query_mut, None)
|
||||
.map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
|
||||
Ok(songs.into_iter().map(song_to_item).collect())
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +120,6 @@ fn str_to_term(s: &str) -> Term<'_> {
|
|||
"file" => Term::File,
|
||||
"base" => Term::Base,
|
||||
"lastmod" => Term::LastMod,
|
||||
x => Term::Tag(x.into())
|
||||
x => Term::Tag(x.into()),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,12 +58,7 @@ impl std::default::Default for DefaultAnalyzer {
|
|||
|
||||
#[cfg(feature = "bliss-audio-symphonia")]
|
||||
impl DefaultAnalyzer {
|
||||
fn request_distance(
|
||||
&mut self,
|
||||
from: &Item,
|
||||
to: &Item,
|
||||
ack: bool,
|
||||
) -> Result<(), RuntimeMsg> {
|
||||
fn request_distance(&mut self, from: &Item, to: &Item, ack: bool) -> Result<(), RuntimeMsg> {
|
||||
let path_from = Self::get_path(from)?;
|
||||
let path_to = Self::get_path(to)?;
|
||||
self.requests
|
||||
|
@ -89,7 +84,6 @@ impl DefaultAnalyzer {
|
|||
PATH_FIELD, path
|
||||
)))
|
||||
}
|
||||
|
||||
} else {
|
||||
Err(RuntimeMsg(format!(
|
||||
"Field {} on item is not String, it's {}",
|
||||
|
@ -141,7 +135,7 @@ impl MusicAnalyzer for DefaultAnalyzer {
|
|||
};
|
||||
}
|
||||
}
|
||||
ResponseType::Song { .. } => {},
|
||||
ResponseType::Song { .. } => {}
|
||||
ResponseType::UnsupportedSong { path, msg } => {
|
||||
if path == path_to || path == path_from {
|
||||
return Err(RuntimeMsg(format!("Bliss error: {}", msg)));
|
||||
|
@ -213,7 +207,7 @@ enum ResponseType {
|
|||
UnsupportedSong {
|
||||
path: String,
|
||||
msg: String,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
#[cfg(feature = "bliss-audio-symphonia")]
|
||||
|
@ -248,11 +242,11 @@ impl CacheThread {
|
|||
distance,
|
||||
} => {
|
||||
self.insert_distance(path1, path2, distance);
|
||||
},
|
||||
}
|
||||
ResponseType::Song { path, song } => {
|
||||
self.insert_song(path, song);
|
||||
},
|
||||
ResponseType::UnsupportedSong { .. } => {},
|
||||
}
|
||||
ResponseType::UnsupportedSong { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -263,7 +257,8 @@ impl CacheThread {
|
|||
// avoid using too much memory -- songs are big memory objects
|
||||
self.song_cache.clear();
|
||||
}
|
||||
self.song_cache.insert(path, song_result.map(|x| x.as_ref().to_owned()));
|
||||
self.song_cache
|
||||
.insert(path, song_result.map(|x| x.as_ref().to_owned()));
|
||||
}
|
||||
|
||||
fn insert_distance(
|
||||
|
@ -309,8 +304,11 @@ impl CacheThread {
|
|||
} else {
|
||||
self.insert_song(path2, song);
|
||||
}
|
||||
},
|
||||
ResponseType::UnsupportedSong {path: unsupported_path, ..} => {
|
||||
}
|
||||
ResponseType::UnsupportedSong {
|
||||
path: unsupported_path,
|
||||
..
|
||||
} => {
|
||||
self.song_in_progress.remove(&unsupported_path);
|
||||
if path == unsupported_path {
|
||||
return None;
|
||||
|
@ -319,10 +317,7 @@ impl CacheThread {
|
|||
}
|
||||
}
|
||||
} else if self.song_cache.contains_key(path) {
|
||||
let result = self
|
||||
.song_cache
|
||||
.get(path)
|
||||
.and_then(|r| r.clone().ok());
|
||||
let result = self.song_cache.get(path).and_then(|r| r.clone().ok());
|
||||
if result.is_none() && auto_add {
|
||||
self.song_in_progress.insert(path.to_owned());
|
||||
}
|
||||
|
@ -346,11 +341,15 @@ impl CacheThread {
|
|||
if let Some(result) = self.distance_cache.get(&key) {
|
||||
if ack {
|
||||
let result = result.to_owned();
|
||||
if self.responses.send(ResponseType::Distance {
|
||||
if self
|
||||
.responses
|
||||
.send(ResponseType::Distance {
|
||||
path1: path1,
|
||||
path2: path2,
|
||||
distance: result,
|
||||
}).is_err() {
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -360,17 +359,26 @@ impl CacheThread {
|
|||
// also prevents deadlock in self.get_song_option()
|
||||
// due to waiting on song that isn't being processed yet
|
||||
// (first call adds it to song_in_progress set, second call just waits)
|
||||
if ack && self.responses.send(ResponseType::Distance {
|
||||
if ack
|
||||
&& self
|
||||
.responses
|
||||
.send(ResponseType::Distance {
|
||||
path1: path1,
|
||||
path2: path2,
|
||||
distance: Ok(0.0),
|
||||
}).is_err() {
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} else if !self.distance_in_progress.contains(&key) {
|
||||
// distance worker uses 3 threads (it's own thread + 1 extra per song) for 2 songs
|
||||
let available_parallelism =
|
||||
(std::thread::available_parallelism().ok().map(|x| x.get()).unwrap_or(DEFAULT_PARALLELISM) * 2) / 3;
|
||||
let available_parallelism = (std::thread::available_parallelism()
|
||||
.ok()
|
||||
.map(|x| x.get())
|
||||
.unwrap_or(DEFAULT_PARALLELISM)
|
||||
* 2)
|
||||
/ 3;
|
||||
let available_parallelism = if available_parallelism != 0 {
|
||||
available_parallelism - 1
|
||||
} else {
|
||||
|
@ -386,14 +394,17 @@ impl CacheThread {
|
|||
distance,
|
||||
} => {
|
||||
self.insert_distance(path1, path2, distance);
|
||||
},
|
||||
}
|
||||
ResponseType::Song { path: path2, song } => {
|
||||
self.insert_song(path2.clone(), song.clone());
|
||||
if self.song_in_progress.len() <= available_parallelism {
|
||||
break 'inner4;
|
||||
}
|
||||
},
|
||||
ResponseType::UnsupportedSong {path: unsupported_path, ..} => {
|
||||
}
|
||||
ResponseType::UnsupportedSong {
|
||||
path: unsupported_path,
|
||||
..
|
||||
} => {
|
||||
self.song_in_progress.remove(&unsupported_path);
|
||||
if self.song_in_progress.len() <= available_parallelism {
|
||||
break 'inner4;
|
||||
|
@ -431,11 +442,15 @@ impl CacheThread {
|
|||
distance.clone(),
|
||||
);
|
||||
if path1_2 == key.0 && path2_2 == key.1 {
|
||||
if self.responses.send(ResponseType::Distance {
|
||||
if self
|
||||
.responses
|
||||
.send(ResponseType::Distance {
|
||||
path1: path1_2,
|
||||
path2: path2_2,
|
||||
distance: distance,
|
||||
}).is_err() {
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break 'inner1;
|
||||
|
@ -443,13 +458,20 @@ impl CacheThread {
|
|||
}
|
||||
ResponseType::Song { path, song } => {
|
||||
self.insert_song(path, song);
|
||||
},
|
||||
ResponseType::UnsupportedSong { path: unsupported_path, msg } => {
|
||||
}
|
||||
ResponseType::UnsupportedSong {
|
||||
path: unsupported_path,
|
||||
msg,
|
||||
} => {
|
||||
self.song_in_progress.remove(&unsupported_path);
|
||||
if self.responses.send(ResponseType::UnsupportedSong {
|
||||
if self
|
||||
.responses
|
||||
.send(ResponseType::UnsupportedSong {
|
||||
path: unsupported_path.clone(),
|
||||
msg: msg
|
||||
}).is_err() {
|
||||
msg: msg,
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if unsupported_path == key.0 || unsupported_path == key.1 {
|
||||
|
@ -476,10 +498,14 @@ impl CacheThread {
|
|||
} else if !path.contains("://") {
|
||||
path
|
||||
} else {
|
||||
if self.responses.send(ResponseType::UnsupportedSong {
|
||||
if self
|
||||
.responses
|
||||
.send(ResponseType::UnsupportedSong {
|
||||
msg: format!("Song path is not a supported URI, it's `{}`", path),
|
||||
path: path,
|
||||
}).is_err() {
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -487,18 +513,25 @@ impl CacheThread {
|
|||
if let Some(song) = self.song_cache.get(&path) {
|
||||
if ack {
|
||||
let song = song.to_owned();
|
||||
if self.responses.send(ResponseType::Song {
|
||||
if self
|
||||
.responses
|
||||
.send(ResponseType::Song {
|
||||
path: path,
|
||||
song: song.map(Box::new),
|
||||
}).is_err() {
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if !self.song_in_progress.contains(&path) {
|
||||
// every song is roughly 2 threads -- Song::from_path(...) spawns a thread
|
||||
let available_parallelism =
|
||||
std::thread::available_parallelism().ok().map(|x| x.get()).unwrap_or(DEFAULT_PARALLELISM) / 2;
|
||||
let available_parallelism = std::thread::available_parallelism()
|
||||
.ok()
|
||||
.map(|x| x.get())
|
||||
.unwrap_or(DEFAULT_PARALLELISM)
|
||||
/ 2;
|
||||
let available_parallelism = if available_parallelism != 0 {
|
||||
available_parallelism - 1
|
||||
} else {
|
||||
|
@ -520,7 +553,7 @@ impl CacheThread {
|
|||
if self.song_in_progress.len() <= available_parallelism {
|
||||
break 'inner2;
|
||||
}
|
||||
},
|
||||
}
|
||||
ResponseType::UnsupportedSong { path, .. } => {
|
||||
self.song_in_progress.remove(&path);
|
||||
if self.song_in_progress.len() <= available_parallelism {
|
||||
|
@ -555,22 +588,33 @@ impl CacheThread {
|
|||
ResponseType::Song { path: path2, song } => {
|
||||
self.insert_song(path2.clone(), song.clone());
|
||||
if path2 == path {
|
||||
if self.responses.send(ResponseType::Song {
|
||||
if self
|
||||
.responses
|
||||
.send(ResponseType::Song {
|
||||
path: path,
|
||||
song: song,
|
||||
}).is_err() {
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break 'inner3;
|
||||
}
|
||||
}
|
||||
ResponseType::UnsupportedSong { path: unsupported_path, msg } => {
|
||||
ResponseType::UnsupportedSong {
|
||||
path: unsupported_path,
|
||||
msg,
|
||||
} => {
|
||||
self.song_in_progress.remove(&unsupported_path);
|
||||
if unsupported_path == path {
|
||||
if self.responses.send(ResponseType::UnsupportedSong {
|
||||
if self
|
||||
.responses
|
||||
.send(ResponseType::UnsupportedSong {
|
||||
path: unsupported_path,
|
||||
msg: msg
|
||||
}).is_err() {
|
||||
msg: msg,
|
||||
})
|
||||
.is_err()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
break 'inner3;
|
||||
|
@ -639,7 +683,11 @@ fn worker_distance(
|
|||
})
|
||||
.unwrap_or(());
|
||||
if new_song2.is_err() {
|
||||
eprintln!("Song error on `{}`: {}", path2, new_song2.clone().err().unwrap());
|
||||
eprintln!(
|
||||
"Song error on `{}`: {}",
|
||||
path2,
|
||||
new_song2.clone().err().unwrap()
|
||||
);
|
||||
}
|
||||
new_song2?
|
||||
};
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use core::fmt::Debug;
|
||||
use std::fmt::Write;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt::Write;
|
||||
|
||||
use crate::lang::db::*;
|
||||
use crate::lang::RuntimeMsg;
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::fmt::{Debug, Display, Error, Formatter};
|
|||
use std::collections::HashMap;
|
||||
|
||||
use crate::lang::Op;
|
||||
use crate::lang::TypePrimitive;
|
||||
use crate::lang::RuntimeMsg;
|
||||
use crate::lang::TypePrimitive;
|
||||
use crate::Item;
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::collections::VecDeque;
|
||||
|
||||
use super::Token;
|
||||
use super::ParseError;
|
||||
use super::Token;
|
||||
|
||||
pub trait TokenReader {
|
||||
fn current_line(&self) -> usize;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//! Integration tests for every syntax feature
|
||||
|
||||
use muss_interpreter::tokens::{Token, Tokenizer, ParseError};
|
||||
use muss_interpreter::tokens::{ParseError, Token, Tokenizer};
|
||||
use muss_interpreter::*;
|
||||
use std::collections::VecDeque;
|
||||
use std::io::Cursor;
|
||||
|
@ -823,45 +823,21 @@ fn execute_emptiesop_line() -> Result<(), InterpreterError> {
|
|||
|
||||
#[test]
|
||||
fn execute_nonemptyfilter_line() -> Result<(), InterpreterError> {
|
||||
execute_single_line(
|
||||
"files().(??)",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line(
|
||||
"empties(42).(??)",
|
||||
true,
|
||||
true,
|
||||
)
|
||||
execute_single_line("files().(??)", false, true)?;
|
||||
execute_single_line("empties(42).(??)", true, true)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn execute_mpdfunction_line() -> Result<(), InterpreterError> {
|
||||
execute_single_line(
|
||||
"mpd(`127.0.0.1:6600`, artist=`Bruno Mars`)",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line("mpd(`127.0.0.1:6600`, artist=`Bruno Mars`)", false, true)?;
|
||||
execute_single_line(
|
||||
"mpd(`127.0.0.1:6600`, title=`something very long that should match absolutely nothing, probably, hopefully...`)",
|
||||
true,
|
||||
true,
|
||||
)?;
|
||||
#[cfg(feature = "ergonomics")]
|
||||
execute_single_line(
|
||||
"mpd(`localhost:6600`)",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line("mpd(`localhost:6600`)", false, true)?;
|
||||
#[cfg(feature = "ergonomics")]
|
||||
execute_single_line(
|
||||
"mpd(`default`)",
|
||||
false,
|
||||
true,
|
||||
)?;
|
||||
execute_single_line(
|
||||
"mpd(`127.0.0.1:6600`)",
|
||||
false,
|
||||
true,
|
||||
)
|
||||
execute_single_line("mpd(`default`)", false, true)?;
|
||||
execute_single_line("mpd(`127.0.0.1:6600`)", false, true)
|
||||
}
|
||||
|
|
|
@ -33,9 +33,7 @@ fn main() {
|
|||
for item in runner {
|
||||
match item {
|
||||
Ok(music) => {
|
||||
if let Some(filename) =
|
||||
music_filename(&music)
|
||||
{
|
||||
if let Some(filename) = music_filename(&music) {
|
||||
playlist.segments.push(MediaSegment {
|
||||
uri: filename,
|
||||
title: music_title(&music),
|
||||
|
@ -56,9 +54,7 @@ fn main() {
|
|||
for item in runner {
|
||||
match item {
|
||||
Ok(music) => {
|
||||
if let Some(filename) =
|
||||
music_filename(&music)
|
||||
{
|
||||
if let Some(filename) = music_filename(&music) {
|
||||
playlist.segments.push(MediaSegment {
|
||||
uri: filename,
|
||||
title: music_title(&music),
|
||||
|
@ -99,5 +95,3 @@ fn music_filename(item: &Item) -> Option<String> {
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::thread::JoinHandle;
|
||||
|
||||
use muss_interpreter::{Item, InterpreterError};
|
||||
use muss_interpreter::{InterpreterError, Item};
|
||||
|
||||
use super::os_controls::SystemControlWrapper;
|
||||
use super::player_wrapper::{ControlAction, PlayerServer, PlayerAction};
|
||||
use super::Player;
|
||||
use super::player_wrapper::{ControlAction, PlayerAction, PlayerServer};
|
||||
use super::PlaybackError;
|
||||
use super::Player;
|
||||
use super::PlayerError;
|
||||
|
||||
/// A controller for a Player running on another thread.
|
||||
|
@ -87,14 +87,15 @@ impl Controller {
|
|||
Ok(())
|
||||
} else {
|
||||
Err(PlaybackError {
|
||||
msg: "Incorrect acknowledgement received for Controller control action"
|
||||
.into(),
|
||||
}.into())
|
||||
msg: "Incorrect acknowledgement received for Controller control action".into(),
|
||||
}
|
||||
.into())
|
||||
}
|
||||
} else {
|
||||
Err(PlaybackError {
|
||||
msg: "Invalid acknowledgement received for Controller control action".into(),
|
||||
}.into())
|
||||
}
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -146,7 +147,8 @@ impl Controller {
|
|||
Ok(x) => Ok(x),
|
||||
Err(_) => Err(PlaybackError {
|
||||
msg: "PlayerServer did not exit correctly".into(),
|
||||
}.into()),
|
||||
}
|
||||
.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -208,10 +210,13 @@ impl Controller {
|
|||
if action == to_send {
|
||||
break;
|
||||
} else {
|
||||
result.push(PlaybackError {
|
||||
result.push(
|
||||
PlaybackError {
|
||||
msg: "Incorrect acknowledgement received for Controller control action"
|
||||
.into(),
|
||||
}.into());
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
} else if let Err(e) = self.handle_event(msg) {
|
||||
result.push(e);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
use std::convert::From;
|
||||
use std::fmt::{Debug, Display, Error, Formatter};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PlayerError {
|
||||
|
@ -67,7 +67,7 @@ impl From<PlaybackError> for PlayerError {
|
|||
#[derive(Debug, Clone)]
|
||||
pub enum UriError {
|
||||
Unsupported(String),
|
||||
Message(String)
|
||||
Message(String),
|
||||
}
|
||||
|
||||
impl UriError {
|
||||
|
@ -81,7 +81,7 @@ impl Display for UriError {
|
|||
write!(f, "UriError: ")?;
|
||||
match self {
|
||||
Self::Unsupported(scheme) => write!(f, "Unsupported URI `{}//`", scheme),
|
||||
Self::Message(msg) => write!(f, "{}", msg)
|
||||
Self::Message(msg) => write!(f, "{}", msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,10 @@ pub(crate) mod uri;
|
|||
//mod utility;
|
||||
|
||||
pub use controller::Controller;
|
||||
pub use errors::{PlaybackError, UriError, PlayerError};
|
||||
pub use player::Player;
|
||||
pub use errors::{PlaybackError, PlayerError, UriError};
|
||||
#[cfg(feature = "mpd")]
|
||||
pub use player::mpd_connection;
|
||||
pub use player::Player;
|
||||
//pub use utility::{play_script};
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
|
@ -179,9 +179,7 @@ impl SystemControlWrapper {
|
|||
|
||||
fn enqueued(item: Item, dbus_ctrl: &Sender<DbusControl>) {
|
||||
//println!("Got enqueued item {}", &item.title);
|
||||
let file_uri = item
|
||||
.field("filename")
|
||||
.and_then(|x| x.to_owned().to_str());
|
||||
let file_uri = item.field("filename").and_then(|x| x.to_owned().to_str());
|
||||
dbus_ctrl
|
||||
.send(DbusControl::SetMetadata(Metadata {
|
||||
length: None,
|
||||
|
|
|
@ -6,11 +6,11 @@ use rodio::{decoder::Decoder, OutputStream, OutputStreamHandle, Sink};
|
|||
use m3u8_rs::{MediaPlaylist, MediaSegment};
|
||||
|
||||
#[cfg(feature = "mpd")]
|
||||
use mpd::{Client, Song, error};
|
||||
use mpd::{error, Client, Song};
|
||||
|
||||
use super::uri::Uri;
|
||||
|
||||
use muss_interpreter::{Item, InterpreterError};
|
||||
use muss_interpreter::{InterpreterError, Item};
|
||||
|
||||
//use super::PlaybackError;
|
||||
use super::PlayerError;
|
||||
|
@ -166,9 +166,7 @@ impl<I: std::iter::Iterator<Item=Result<Item, InterpreterError>>> Player<I> {
|
|||
for item in &mut self.runner {
|
||||
match item {
|
||||
Ok(music) => {
|
||||
if let Some(filename) =
|
||||
music_filename(&music)
|
||||
{
|
||||
if let Some(filename) = music_filename(&music) {
|
||||
//println!("Adding file `{}` to playlist", filename);
|
||||
playlist.segments.push(MediaSegment {
|
||||
uri: filename,
|
||||
|
@ -215,12 +213,13 @@ 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.path()).map_err(PlayerError::from_err_playback)?;
|
||||
let file =
|
||||
fs::File::open(uri.path()).map_err(PlayerError::from_err_playback)?;
|
||||
let stream = io::BufReader::new(file);
|
||||
let source = Decoder::new(stream).map_err(PlayerError::from_err_playback)?;
|
||||
self.sink.append(source);
|
||||
Ok(())
|
||||
},
|
||||
}
|
||||
#[cfg(feature = "mpd")]
|
||||
"mpd:" => {
|
||||
if let Some(mpd_client) = &mut self.mpd_connection {
|
||||
|
@ -229,13 +228,17 @@ impl<I: std::iter::Iterator<Item=Result<Item, InterpreterError>>> Player<I> {
|
|||
file: uri.path().to_owned(),
|
||||
..Default::default()
|
||||
};
|
||||
mpd_client.push(song).map_err(PlayerError::from_err_playback)?;
|
||||
mpd_client
|
||||
.push(song)
|
||||
.map_err(PlayerError::from_err_playback)?;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(PlayerError::from_err_playback("Cannot play MPD song: no MPD client connected"))
|
||||
Err(PlayerError::from_err_playback(
|
||||
"Cannot play MPD song: no MPD client connected",
|
||||
))
|
||||
}
|
||||
},
|
||||
scheme => Err(UriError::Unsupported(scheme.to_owned()).into())
|
||||
}
|
||||
scheme => Err(UriError::Unsupported(scheme.to_owned()).into()),
|
||||
},
|
||||
None => {
|
||||
//default
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::mpsc::{Receiver, Sender};
|
|||
use std::{thread, thread::JoinHandle};
|
||||
|
||||
//use muss_interpreter::tokens::TokenReader;
|
||||
use muss_interpreter::{Item, InterpreterError};
|
||||
use muss_interpreter::{InterpreterError, Item};
|
||||
|
||||
use super::Player;
|
||||
use super::PlayerError;
|
||||
|
|
|
@ -22,8 +22,8 @@ impl<'a> Uri<&'a str> {
|
|||
} else {
|
||||
self.0.get(end + 2..).unwrap()
|
||||
}
|
||||
},
|
||||
None => self.0
|
||||
}
|
||||
None => self.0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,9 @@ pub fn parse() -> CliArgs {
|
|||
|
||||
pub fn validate(args: &CliArgs) -> Result<(), String> {
|
||||
if let Some(mpd_addr) = &args.mpd {
|
||||
let _: std::net::SocketAddr = mpd_addr.parse().map_err(|e| format!("Unrecognized MPS address `{}`: {}", mpd_addr, e))?;
|
||||
let _: std::net::SocketAddr = mpd_addr
|
||||
.parse()
|
||||
.map_err(|e| format!("Unrecognized MPS address `{}`: {}", mpd_addr, e))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -81,7 +81,12 @@ fn main() {
|
|||
// build playback controller
|
||||
let script_file2 = script_file.clone();
|
||||
let volume = args.volume;
|
||||
let mpd = match args.mpd.clone().map(|a| muss_player::mpd_connection(a.parse().unwrap())).transpose() {
|
||||
let mpd = match args
|
||||
.mpd
|
||||
.clone()
|
||||
.map(|a| muss_player::mpd_connection(a.parse().unwrap()))
|
||||
.transpose()
|
||||
{
|
||||
Ok(mpd) => mpd,
|
||||
Err(e) => panic!("Abort: Cannot connect to MPD: {}", e),
|
||||
};
|
||||
|
|
218
src/repl.rs
218
src/repl.rs
|
@ -1,27 +1,25 @@
|
|||
//! Read, Execute, Print Loop functionality
|
||||
#![allow(clippy::single_match)]
|
||||
use std::sync::{RwLock};
|
||||
use std::sync::mpsc::{self, Receiver};
|
||||
use std::io::{self, Write};
|
||||
use std::sync::mpsc::{self, Receiver};
|
||||
use std::sync::RwLock;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
|
||||
use console::{Key, Term};
|
||||
|
||||
use muss_interpreter::{Interpreter, Debugger, Item, InterpreterEvent, InterpreterError};
|
||||
use muss_interpreter::lang::TypePrimitive;
|
||||
use muss_interpreter::{Debugger, Interpreter, InterpreterError, InterpreterEvent, Item};
|
||||
use muss_player::{Controller, Player};
|
||||
|
||||
use super::channel_io::{channel_io, ChannelWriter};
|
||||
use super::cli::CliArgs;
|
||||
|
||||
lazy_static! {
|
||||
static ref DEBUG_STATE: RwLock<DebugState> = RwLock::new(
|
||||
DebugState {
|
||||
static ref DEBUG_STATE: RwLock<DebugState> = RwLock::new(DebugState {
|
||||
debug_flag: DebugFlag::Normal,
|
||||
verbose: false,
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
const TERMINAL_WRITE_ERROR: &str = "Failed to write to terminal output";
|
||||
|
@ -55,7 +53,7 @@ struct DebugState {
|
|||
enum DebugFlag {
|
||||
Skip,
|
||||
List,
|
||||
Normal
|
||||
Normal,
|
||||
}
|
||||
|
||||
impl ReplState {
|
||||
|
@ -80,59 +78,79 @@ impl ReplState {
|
|||
}
|
||||
}
|
||||
|
||||
fn interpreter_event_callback<T: muss_interpreter::tokens::TokenReader>(_interpreter: &mut Interpreter<'_, T>, event: InterpreterEvent) -> Result<(), InterpreterError> {
|
||||
fn interpreter_event_callback<T: muss_interpreter::tokens::TokenReader>(
|
||||
_interpreter: &mut Interpreter<'_, T>,
|
||||
event: InterpreterEvent,
|
||||
) -> Result<(), InterpreterError> {
|
||||
match event {
|
||||
InterpreterEvent::StatementComplete => {
|
||||
if let Ok(mut d_state) = DEBUG_STATE.write() {
|
||||
d_state.debug_flag = DebugFlag::Normal;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn item_prompt(terminal: &mut Term, args: &CliArgs) {
|
||||
write!(terminal, "*I{}", args.prompt)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
write!(terminal, "*I{}", args.prompt).expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
|
||||
fn pretty_print_item(item: &Item, terminal: &mut Term, args: &CliArgs, verbose: bool) {
|
||||
item_prompt(terminal, args);
|
||||
if verbose {
|
||||
writeln!(terminal, "--\\/-- `{}` --\\/--",
|
||||
item.field("title").unwrap_or(&TypePrimitive::Empty).as_str()
|
||||
).expect(TERMINAL_WRITE_ERROR);
|
||||
writeln!(
|
||||
terminal,
|
||||
"--\\/-- `{}` --\\/--",
|
||||
item.field("title")
|
||||
.unwrap_or(&TypePrimitive::Empty)
|
||||
.as_str()
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
let mut fields: Vec<&_> = item.iter().collect();
|
||||
fields.sort();
|
||||
for field in fields {
|
||||
if field != "title" {
|
||||
writeln!(terminal, " {}: `{}`",
|
||||
writeln!(
|
||||
terminal,
|
||||
" {}: `{}`",
|
||||
field,
|
||||
item.field(field).unwrap_or(&TypePrimitive::Empty).as_str()
|
||||
).expect(TERMINAL_WRITE_ERROR);
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeln!(terminal, "`{}` by `{}`",
|
||||
item.field("title").unwrap_or(&TypePrimitive::Empty).as_str(),
|
||||
item.field("artist").unwrap_or(&TypePrimitive::Empty).as_str(),
|
||||
).expect(TERMINAL_WRITE_ERROR);
|
||||
writeln!(
|
||||
terminal,
|
||||
"`{}` by `{}`",
|
||||
item.field("title")
|
||||
.unwrap_or(&TypePrimitive::Empty)
|
||||
.as_str(),
|
||||
item.field("artist")
|
||||
.unwrap_or(&TypePrimitive::Empty)
|
||||
.as_str(),
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
//writeln!(terminal, "I{}----", args.prompt).expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
|
||||
fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
|
||||
//let items = state.list_rx.try_iter().collect::<Vec<_>>();
|
||||
let d_state = DEBUG_STATE.read().expect("Failed to get read lock for debug state info").clone();
|
||||
let d_state = DEBUG_STATE
|
||||
.read()
|
||||
.expect("Failed to get read lock for debug state info")
|
||||
.clone();
|
||||
for item in state.list_rx.try_iter() {
|
||||
match item {
|
||||
Ok(item) => pretty_print_item(&item, &mut state.terminal, args, d_state.verbose),
|
||||
Err(e) => error_prompt(
|
||||
muss_player::PlayerError::Playback(
|
||||
muss_player::PlaybackError::from_err(e)
|
||||
), args),
|
||||
muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)),
|
||||
args,
|
||||
),
|
||||
}
|
||||
}
|
||||
let flag = d_state.debug_flag;
|
||||
|
@ -140,11 +158,13 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
|
|||
DebugFlag::List => {
|
||||
while let Ok(item) = state.list_rx.recv() {
|
||||
match item {
|
||||
Ok(item) => pretty_print_item(&item, &mut state.terminal, args, d_state.verbose),
|
||||
Ok(item) => {
|
||||
pretty_print_item(&item, &mut state.terminal, args, d_state.verbose)
|
||||
}
|
||||
Err(e) => error_prompt(
|
||||
muss_player::PlayerError::Playback(
|
||||
muss_player::PlaybackError::from_err(e)
|
||||
), args),
|
||||
muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)),
|
||||
args,
|
||||
),
|
||||
}
|
||||
// stop listing if no longer in list mode
|
||||
let flag = if let Ok(d_state) = DEBUG_STATE.read() {
|
||||
|
@ -153,11 +173,11 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
|
|||
DebugFlag::Normal
|
||||
};
|
||||
match flag {
|
||||
DebugFlag::List => {},
|
||||
DebugFlag::List => {}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -167,18 +187,26 @@ pub fn repl(args: CliArgs) {
|
|||
term.set_title("muss");
|
||||
let (writer, reader) = channel_io();
|
||||
let volume = args.volume;
|
||||
let mpd = match args.mpd.clone().map(|a| muss_player::mpd_connection(a.parse().unwrap())).transpose() {
|
||||
let mpd = match args
|
||||
.mpd
|
||||
.clone()
|
||||
.map(|a| muss_player::mpd_connection(a.parse().unwrap()))
|
||||
.transpose()
|
||||
{
|
||||
Ok(mpd) => mpd,
|
||||
Err(e) => {
|
||||
eprintln!("Cannot connect to MPD address `{}`: {}", args.mpd.unwrap(), e);
|
||||
eprintln!(
|
||||
"Cannot connect to MPD address `{}`: {}",
|
||||
args.mpd.unwrap(),
|
||||
e
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let (list_tx, list_rx) = mpsc::channel();
|
||||
let mut state = ReplState::new(writer, term, list_rx);
|
||||
let player_builder = move || {
|
||||
let runner = Interpreter::with_stream_and_callback(reader,
|
||||
&interpreter_event_callback);
|
||||
let runner = Interpreter::with_stream_and_callback(reader, &interpreter_event_callback);
|
||||
let debugger = Debugger::new(runner, move |interpretor, item| {
|
||||
let flag = if let Ok(d_state) = DEBUG_STATE.read() {
|
||||
d_state.debug_flag
|
||||
|
@ -192,7 +220,7 @@ pub fn repl(args: CliArgs) {
|
|||
// NOTE: recursion occurs here
|
||||
}
|
||||
None
|
||||
},
|
||||
}
|
||||
DebugFlag::List => {
|
||||
if let Some(x) = item {
|
||||
list_tx.send(x.map_err(|e| e.to_string())).unwrap_or(());
|
||||
|
@ -286,7 +314,11 @@ pub fn repl(args: CliArgs) {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut ReplState, mut execute: F) -> ! {
|
||||
fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(
|
||||
args: &CliArgs,
|
||||
state: &mut ReplState,
|
||||
mut execute: F,
|
||||
) -> ! {
|
||||
prompt(state, args);
|
||||
loop {
|
||||
match state
|
||||
|
@ -296,13 +328,11 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
{
|
||||
Key::Char(read_c) => {
|
||||
if state.cursor_rightward_position == 0 {
|
||||
write!(state.terminal, "{}", read_c)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
write!(state.terminal, "{}", read_c).expect(TERMINAL_WRITE_ERROR);
|
||||
state.statement_buf.push(read_c);
|
||||
state.current_line.push(read_c);
|
||||
} else {
|
||||
write!(state.terminal, "{}", read_c)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
write!(state.terminal, "{}", read_c).expect(TERMINAL_WRITE_ERROR);
|
||||
for i in state.current_line.len() - state.cursor_rightward_position
|
||||
..state.current_line.len()
|
||||
{
|
||||
|
@ -346,21 +376,22 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
}
|
||||
';' => {
|
||||
if state.in_literal.is_none() {
|
||||
state
|
||||
.terminal
|
||||
.write_line("")
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
state.terminal.write_line("").expect(TERMINAL_WRITE_ERROR);
|
||||
let statement = state.statement_buf.iter().collect::<String>();
|
||||
let statement_result = statement.trim();
|
||||
if !statement_result.starts_with('?') {
|
||||
state
|
||||
.writer
|
||||
.write_all(state.statement_buf.iter().collect::<String>().as_bytes())
|
||||
.write_all(
|
||||
state.statement_buf.iter().collect::<String>().as_bytes(),
|
||||
)
|
||||
.expect(INTERPRETER_WRITE_ERROR);
|
||||
execute(state, args);
|
||||
state.statement_buf.clear();
|
||||
}
|
||||
let last_line = state.current_line[0..state.current_line.len() - 1].iter().collect::<String>();
|
||||
let last_line = state.current_line[0..state.current_line.len() - 1]
|
||||
.iter()
|
||||
.collect::<String>();
|
||||
state.current_line.clear();
|
||||
if !last_line.is_empty()
|
||||
&& ((!state.history.is_empty()
|
||||
|
@ -415,8 +446,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
.terminal
|
||||
.move_cursor_left(1)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
write!(state.terminal, " ")
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
write!(state.terminal, " ").expect(TERMINAL_WRITE_ERROR);
|
||||
state
|
||||
.terminal
|
||||
.flush()
|
||||
|
@ -433,9 +463,9 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
let removed_char = state
|
||||
.current_line
|
||||
.remove(state.current_line.len() - state.cursor_rightward_position - 1);
|
||||
state.statement_buf.remove(
|
||||
state.statement_buf.len() - state.cursor_rightward_position - 1,
|
||||
);
|
||||
state
|
||||
.statement_buf
|
||||
.remove(state.statement_buf.len() - state.cursor_rightward_position - 1);
|
||||
// re-sync unclosed syntax tracking
|
||||
match removed_char {
|
||||
'"' | '`' => {
|
||||
|
@ -484,9 +514,9 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
let removed_char = state
|
||||
.current_line
|
||||
.remove(state.current_line.len() - state.cursor_rightward_position);
|
||||
state.statement_buf.remove(
|
||||
state.statement_buf.len() - state.cursor_rightward_position,
|
||||
);
|
||||
state
|
||||
.statement_buf
|
||||
.remove(state.statement_buf.len() - state.cursor_rightward_position);
|
||||
// re-sync unclosed syntax tracking
|
||||
match removed_char {
|
||||
'"' | '`' => {
|
||||
|
@ -528,10 +558,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
}
|
||||
}
|
||||
Key::Enter => {
|
||||
state
|
||||
.terminal
|
||||
.write_line("")
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
state.terminal.write_line("").expect(TERMINAL_WRITE_ERROR);
|
||||
let statement = state.statement_buf.iter().collect::<String>();
|
||||
let statement_result = statement.trim();
|
||||
if statement_result.starts_with('?') {
|
||||
|
@ -578,10 +605,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
1 => {
|
||||
state.selected_history = 0;
|
||||
state.line_number -= 1;
|
||||
state
|
||||
.terminal
|
||||
.clear_line()
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
state.terminal.clear_line().expect(TERMINAL_WRITE_ERROR);
|
||||
prompt(state, args);
|
||||
// clear stale input buffer
|
||||
state.statement_buf.clear();
|
||||
|
@ -589,12 +613,12 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
state.in_literal = None;
|
||||
state.bracket_depth = 0;
|
||||
state.curly_depth = 0;
|
||||
},
|
||||
0 => {},
|
||||
}
|
||||
0 => {}
|
||||
_ => {
|
||||
state.selected_history -= 1;
|
||||
display_history_line(state, args);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Key::ArrowLeft => {
|
||||
|
@ -624,8 +648,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
|
|||
|
||||
#[inline(always)]
|
||||
fn prompt(state: &mut ReplState, args: &CliArgs) {
|
||||
write!(state.terminal, "{: >2}{}", state.line_number, args.prompt)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
write!(state.terminal, "{: >2}{}", state.line_number, args.prompt).expect(TERMINAL_WRITE_ERROR);
|
||||
state.line_number += 1;
|
||||
state
|
||||
.terminal
|
||||
|
@ -637,10 +660,7 @@ fn prompt(state: &mut ReplState, args: &CliArgs) {
|
|||
fn display_history_line(state: &mut ReplState, args: &CliArgs) {
|
||||
// get historical line
|
||||
state.line_number -= 1;
|
||||
state
|
||||
.terminal
|
||||
.clear_line()
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
state.terminal.clear_line().expect(TERMINAL_WRITE_ERROR);
|
||||
prompt(state, args);
|
||||
let new_statement = state.history[state.history.len() - state.selected_history].trim();
|
||||
state
|
||||
|
@ -665,42 +685,62 @@ fn error_prompt(error: muss_player::PlayerError, args: &CliArgs) {
|
|||
fn repl_commands(command_str: &str, state: &mut ReplState, _args: &CliArgs) {
|
||||
let words: Vec<&str> = command_str.split(' ').map(|s| s.trim()).collect();
|
||||
match words[0] {
|
||||
"?help" => writeln!(state.terminal, "{}", super::help::HELP_STRING).expect(TERMINAL_WRITE_ERROR),
|
||||
"?function" | "?functions" => writeln!(state.terminal, "{}", super::help::FUNCTIONS).expect(TERMINAL_WRITE_ERROR),
|
||||
"?filter" | "?filters" => writeln!(state.terminal, "{}", super::help::FILTERS).expect(TERMINAL_WRITE_ERROR),
|
||||
"?sort" | "?sorter" | "?sorters" => writeln!(state.terminal, "{}", super::help::SORTERS).expect(TERMINAL_WRITE_ERROR),
|
||||
"?proc" | "?procedure" | "?procedures" => writeln!(state.terminal, "{}", super::help::PROCEDURES).expect(TERMINAL_WRITE_ERROR),
|
||||
"?help" => {
|
||||
writeln!(state.terminal, "{}", super::help::HELP_STRING).expect(TERMINAL_WRITE_ERROR)
|
||||
}
|
||||
"?function" | "?functions" => {
|
||||
writeln!(state.terminal, "{}", super::help::FUNCTIONS).expect(TERMINAL_WRITE_ERROR)
|
||||
}
|
||||
"?filter" | "?filters" => {
|
||||
writeln!(state.terminal, "{}", super::help::FILTERS).expect(TERMINAL_WRITE_ERROR)
|
||||
}
|
||||
"?sort" | "?sorter" | "?sorters" => {
|
||||
writeln!(state.terminal, "{}", super::help::SORTERS).expect(TERMINAL_WRITE_ERROR)
|
||||
}
|
||||
"?proc" | "?procedure" | "?procedures" => {
|
||||
writeln!(state.terminal, "{}", super::help::PROCEDURES).expect(TERMINAL_WRITE_ERROR)
|
||||
}
|
||||
"?list" => {
|
||||
{
|
||||
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
|
||||
let mut debug_state = DEBUG_STATE
|
||||
.write()
|
||||
.expect("Failed to get write lock for debug state");
|
||||
debug_state.debug_flag = DebugFlag::List;
|
||||
}
|
||||
writeln!(state.terminal, "Listing upcoming items").expect(TERMINAL_WRITE_ERROR);
|
||||
|
||||
},
|
||||
}
|
||||
"?skip" => {
|
||||
{
|
||||
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
|
||||
let mut debug_state = DEBUG_STATE
|
||||
.write()
|
||||
.expect("Failed to get write lock for debug state");
|
||||
debug_state.debug_flag = DebugFlag::Skip;
|
||||
}
|
||||
writeln!(state.terminal, "Skipping upcoming items").expect(TERMINAL_WRITE_ERROR);
|
||||
},
|
||||
}
|
||||
"?normal" => {
|
||||
{
|
||||
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
|
||||
let mut debug_state = DEBUG_STATE
|
||||
.write()
|
||||
.expect("Failed to get write lock for debug state");
|
||||
debug_state.debug_flag = DebugFlag::Normal;
|
||||
}
|
||||
writeln!(state.terminal, "Resuming normal operation").expect(TERMINAL_WRITE_ERROR);
|
||||
},
|
||||
}
|
||||
"?verbose" => {
|
||||
let verbose = {
|
||||
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
|
||||
let mut debug_state = DEBUG_STATE
|
||||
.write()
|
||||
.expect("Failed to get write lock for debug state");
|
||||
debug_state.verbose = !debug_state.verbose;
|
||||
debug_state.verbose
|
||||
};
|
||||
writeln!(state.terminal, "Verbosed toggled to {}", verbose).expect(TERMINAL_WRITE_ERROR);
|
||||
},
|
||||
"?commands" => writeln!(state.terminal, "{}", super::help::REPL_COMMANDS).expect(TERMINAL_WRITE_ERROR),
|
||||
writeln!(state.terminal, "Verbosed toggled to {}", verbose)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
"?commands" => {
|
||||
writeln!(state.terminal, "{}", super::help::REPL_COMMANDS).expect(TERMINAL_WRITE_ERROR)
|
||||
}
|
||||
_ => writeln!(state.terminal, "Unknown command, try ?help").expect(TERMINAL_WRITE_ERROR),
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue