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