cargo fmt

This commit is contained in:
NGnius (Graham) 2022-07-30 00:06:21 -04:00
parent 21f7149660
commit 175d304f1b
52 changed files with 495 additions and 434 deletions

View file

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

View file

@ -2,7 +2,7 @@
use super::processing::advanced::{DefaultAnalyzer, MusicAnalyzer};
use super::processing::database::{DatabaseQuerier, SQLiteExecutor};
#[cfg(feature = "mpd")]
use super::processing::database::{MpdQuerier, MpdExecutor};
use super::processing::database::{MpdExecutor, MpdQuerier};
use super::processing::general::{
FilesystemExecutor, FilesystemQuerier, OpStorage, VariableStorer,
};

View file

@ -7,10 +7,7 @@ use super::{Interpreter, InterpreterItem};
pub struct Debugger<'a, T, F>
where
T: TokenReader,
F: Fn(
&mut Interpreter<'a, T>,
Option<InterpreterItem>,
) -> Option<InterpreterItem>,
F: Fn(&mut Interpreter<'a, T>, Option<InterpreterItem>) -> Option<InterpreterItem>,
{
interpreter: Interpreter<'a, T>,
transmuter: F,
@ -19,16 +16,10 @@ where
impl<'a, T, F> Debugger<'a, T, F>
where
T: TokenReader,
F: Fn(
&mut Interpreter<'a, T>,
Option<InterpreterItem>,
) -> Option<InterpreterItem>,
F: Fn(&mut Interpreter<'a, T>, Option<InterpreterItem>) -> Option<InterpreterItem>,
{
/// Create a new instance of Debugger using the provided interpreter and callback.
pub fn new(
faye: Interpreter<'a, T>,
item_handler: F,
) -> Self {
pub fn new(faye: Interpreter<'a, T>, item_handler: F) -> Self {
Self {
interpreter: faye,
transmuter: item_handler,
@ -39,10 +30,7 @@ where
impl<'a, T, F> Iterator for Debugger<'a, T, F>
where
T: TokenReader,
F: Fn(
&mut Interpreter<'a, T>,
Option<InterpreterItem>,
) -> Option<InterpreterItem>,
F: Fn(&mut Interpreter<'a, T>, Option<InterpreterItem>) -> Option<InterpreterItem>,
{
type Item = InterpreterItem;

View file

@ -61,7 +61,13 @@ impl<'a, R: Read> Interpreter<'a, Tokenizer<R>> {
Self::with_standard_vocab(tokenizer)
}
pub fn with_stream_and_callback(stream: R, callback: &'a dyn Fn(&mut Interpreter<'a, Tokenizer<R>>, InterpreterEvent) -> Result<(), InterpreterError>) -> Self {
pub fn with_stream_and_callback(
stream: R,
callback: &'a dyn Fn(
&mut Interpreter<'a, Tokenizer<R>>,
InterpreterEvent,
) -> Result<(), InterpreterError>,
) -> Self {
let tokenizer = Tokenizer::new(stream);
let vocab = LanguageDictionary::standard();
Self::with(vocab, tokenizer, callback)
@ -89,7 +95,10 @@ where
pub fn with(
vocab: LanguageDictionary,
token_reader: T,
callback: &'a dyn Fn(&mut Interpreter<'a, T>, InterpreterEvent) -> Result<(), InterpreterError>,
callback: &'a dyn Fn(
&mut Interpreter<'a, T>,
InterpreterEvent,
) -> Result<(), InterpreterError>,
) -> Self {
Self {
tokenizer: token_reader,
@ -190,7 +199,10 @@ where
}
}
fn error_with_ctx<T: std::convert::Into<InterpreterError>>(error: T, line: usize) -> InterpreterError {
fn error_with_ctx<T: std::convert::Into<InterpreterError>>(
error: T,
line: usize,
) -> InterpreterError {
let mut err = error.into();
err.set_line(line);
err

View file

@ -237,15 +237,14 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
// handle other filters
// make fake inner item
let single_op = SingleItem::new_ok(item.clone());
match ctx.variables.declare(
INNER_VARIABLE_NAME,
Type::Op(Box::new(single_op)),
) {
match ctx
.variables
.declare(INNER_VARIABLE_NAME, Type::Op(Box::new(single_op)))
{
Ok(x) => x,
Err(e) => {
//self.context = Some(op.escape());
maybe_result =
Some(Err(e.with(RuntimeOp(fake))));
maybe_result = Some(Err(e.with(RuntimeOp(fake))));
self.context = Some(ctx);
break;
}
@ -268,9 +267,8 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
Ok(_) => {}
Err(e) => match maybe_result {
Some(Ok(_)) => {
maybe_result = Some(Err(
e.with(RuntimeOp(fake))
))
maybe_result =
Some(Err(e.with(RuntimeOp(fake))))
}
Some(Err(e2)) => maybe_result = Some(Err(e2)), // already failing, do not replace error,
None => {} // impossible
@ -332,8 +330,8 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
}
Err(e) => {
self.is_failing = true; // this is unrecoverable and reproducible, so it shouldn't be tried again (to prevent error spam)
return Some(Err(e.with(RuntimeOp(fake))))
},
return Some(Err(e.with(RuntimeOp(fake))));
}
};
let mut maybe_result = None;
let ctx = self.context.take().unwrap();
@ -457,17 +455,12 @@ impl<P: FilterPredicate + 'static> Iterator for FilterStatement<P> {
}
}
pub struct FilterStatementFactory<
P: FilterPredicate + 'static,
F: FilterFactory<P> + 'static,
> {
pub struct FilterStatementFactory<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> {
filter_factory: F,
idc: PhantomData<P>,
}
impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static>
FilterStatementFactory<P, F>
{
impl<P: FilterPredicate + 'static, F: FilterFactory<P> + 'static> FilterStatementFactory<P, F> {
pub fn new(factory: F) -> Self {
Self {
filter_factory: factory,

View file

@ -354,10 +354,7 @@ fn declare_or_replace_item(
Ok(old_item)
}
fn remove_or_replace_item(
old_item: Option<Type>,
ctx: &mut Context,
) -> Result<(), RuntimeMsg> {
fn remove_or_replace_item(old_item: Option<Type>, ctx: &mut Context) -> Result<(), RuntimeMsg> {
ctx.variables.remove(ITEM_VARIABLE_NAME)?;
if let Some(old_item) = old_item {
ctx.variables.declare(ITEM_VARIABLE_NAME, old_item)?;

View file

@ -189,10 +189,7 @@ impl Iterator for ItemBlockStatement {
return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!(
"Expected `item` like Type::Item(Item[...]), got {}",
x
),
msg: format!("Expected `item` like Type::Item(Item[...]), got {}", x),
}))
}
None => {}
@ -214,10 +211,7 @@ pub struct ItemBlockFactory {
}
impl ItemBlockFactory {
pub fn push<
T: ItemOpFactory<Y> + 'static,
Y: Deref<Target = dyn ItemOp> + ItemOp + 'static,
>(
pub fn push<T: ItemOpFactory<Y> + 'static, Y: Deref<Target = dyn ItemOp> + ItemOp + 'static>(
mut self,
factory: T,
) -> Self {
@ -314,10 +308,7 @@ fn replace_item_var(ctx: &mut Context, item: Type) -> Option<Type> {
old_var
}
fn restore_item_var(
ctx: &mut Context,
old_var: Option<Type>,
) -> Result<Option<Type>, RuntimeMsg> {
fn restore_item_var(ctx: &mut Context, old_var: Option<Type>) -> Result<Option<Type>, RuntimeMsg> {
let new_var = if ctx.variables.exists(ITEM_VARIABLE_NAME) {
Some(ctx.variables.remove(ITEM_VARIABLE_NAME)?)
} else {

View file

@ -20,9 +20,7 @@ pub(crate) mod utility;
pub use dictionary::LanguageDictionary;
pub(crate) use error::LanguageError;
pub use error::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
pub use filter::{
FilterFactory, FilterPredicate, FilterStatement, FilterStatementFactory,
};
pub use filter::{FilterFactory, FilterPredicate, FilterStatement, FilterStatementFactory};
pub use filter_replace::FilterReplaceStatement;
pub use function::{FunctionFactory, FunctionStatementFactory};
pub use iter_block::{ItemBlockFactory, ItemOp, ItemOpFactory};

View file

@ -1,8 +1,8 @@
use std::collections::VecDeque;
use std::path::PathBuf;
use super::TypePrimitive;
use super::SyntaxError;
use super::TypePrimitive;
use crate::tokens::Token;
pub fn assert_token<T, F: FnOnce(Token) -> Option<T>>(
@ -29,10 +29,7 @@ pub fn assert_token<T, F: FnOnce(Token) -> Option<T>>(
}
}
pub fn assert_token_raw(
token: Token,
tokens: &mut VecDeque<Token>,
) -> Result<Token, SyntaxError> {
pub fn assert_token_raw(token: Token, tokens: &mut VecDeque<Token>) -> Result<Token, SyntaxError> {
let result = match tokens.pop_front() {
Some(x) => Ok(x),
None => Err(SyntaxError {

View file

@ -5,8 +5,8 @@ use std::iter::Iterator;
use crate::tokens::Token;
use crate::Context;
use crate::lang::{Lookup, LanguageDictionary, PseudoOp};
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op};
use crate::lang::{LanguageDictionary, Lookup, PseudoOp};
use crate::lang::{RuntimeError, RuntimeOp, SyntaxError};
use crate::processing::general::Type;
use crate::Item;

View file

@ -47,11 +47,7 @@ impl Display for FieldFilter {
}
impl FilterPredicate for FieldFilter {
fn matches(
&mut self,
music_item_lut: &Item,
ctx: &mut Context,
) -> Result<bool, RuntimeMsg> {
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
let variable = match &self.val {
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
Type::Primitive(t) => Ok(t),

View file

@ -40,11 +40,7 @@ impl Display for FieldLikeFilter {
}
impl FilterPredicate for FieldLikeFilter {
fn matches(
&mut self,
music_item_lut: &Item,
ctx: &mut Context,
) -> Result<bool, RuntimeMsg> {
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
let variable = match &self.val {
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
Type::Primitive(TypePrimitive::String(s)) => Ok(s),
@ -122,13 +118,11 @@ impl FilterFactory<FieldLikeFilter> for FieldLikeFilterFactory {
};
let name = assert_token(
|t| match t {
Token::Name(s) => {
match &s as _ {
"unlike" | "like" => Some(s),
_ => None,
}
Token::Name(s) => match &s as _ {
"unlike" | "like" => Some(s),
_ => None,
},
_ => None
_ => None,
},
Token::Literal("like|unlike".into()),
tokens,

View file

@ -33,11 +33,7 @@ impl Display for FieldRegexFilter {
}
impl FilterPredicate for FieldRegexFilter {
fn matches(
&mut self,
music_item_lut: &Item,
ctx: &mut Context,
) -> Result<bool, RuntimeMsg> {
fn matches(&mut self, music_item_lut: &Item, ctx: &mut Context) -> Result<bool, RuntimeMsg> {
let variable = match &self.val {
VariableOrValue::Variable(name) => match ctx.variables.get(name)? {
Type::Primitive(TypePrimitive::String(s)) => Ok(s),

View file

@ -2,8 +2,8 @@ use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::LanguageDictionary;
use crate::lang::{utility::assert_token_raw, RuntimeMsg, SyntaxError};
use crate::lang::{FilterFactory, FilterPredicate, FilterStatementFactory};
use crate::lang::{RuntimeMsg, SyntaxError, utility::assert_token_raw};
use crate::tokens::Token;
use crate::Context;
use crate::Item;
@ -57,7 +57,8 @@ impl FilterFactory<NonEmptyFilter> for NonEmptyFilterFactory {
}
}
pub type NonEmptyFilterStatementFactory = FilterStatementFactory<NonEmptyFilter, NonEmptyFilterFactory>;
pub type NonEmptyFilterStatementFactory =
FilterStatementFactory<NonEmptyFilter, NonEmptyFilterFactory>;
#[inline(always)]
pub fn nonempty_filter() -> NonEmptyFilterStatementFactory {

View file

@ -151,8 +151,7 @@ pub fn unique_field_filter() -> UniqueFieldFilterStatementFactory {
UniqueFieldFilterStatementFactory::new(UniqueFilterFactory)
}
pub type UniqueFilterStatementFactory =
FilterStatementFactory<UniqueFilter, UniqueFilterFactory>;
pub type UniqueFilterStatementFactory = FilterStatementFactory<UniqueFilter, UniqueFilterFactory>;
#[inline(always)]
pub fn unique_filter() -> UniqueFilterStatementFactory {

View file

@ -35,9 +35,7 @@ impl ItemOp for AddItemOp {
if let Type::Primitive(lhs) = &lhs {
let rhs = self.rhs.execute(context)?;
if let Type::Primitive(rhs) = &rhs {
Ok(Type::Primitive(
lhs.try_add(rhs).map_err(RuntimeMsg)?,
))
Ok(Type::Primitive(lhs.try_add(rhs).map_err(RuntimeMsg)?))
} else {
Err(RuntimeMsg(format!(
"Cannot add right-hand side `{}` ({}): not primitive type",

View file

@ -78,7 +78,7 @@ impl ItemOpFactory<FieldAssignItemOp> for FieldAssignItemOpFactory {
factory: &ItemBlockFactory,
dict: &LanguageDictionary,
) -> Result<FieldAssignItemOp, SyntaxError> {
let var_name =if tokens[0].is_dot() {
let var_name = if tokens[0].is_dot() {
"item".to_string()
} else {
assert_token(

View file

@ -32,9 +32,7 @@ impl ItemOp for NegateItemOp {
fn execute(&self, context: &mut Context) -> Result<Type, RuntimeMsg> {
let rhs = self.rhs.execute(context)?;
if let Type::Primitive(rhs) = &rhs {
Ok(Type::Primitive(
rhs.try_negate().map_err(RuntimeMsg)?,
))
Ok(Type::Primitive(rhs.try_negate().map_err(RuntimeMsg)?))
} else {
Err(RuntimeMsg(format!(
"Cannot negate `{}` ({}): not primitive type",

View file

@ -32,9 +32,7 @@ impl ItemOp for NotItemOp {
fn execute(&self, context: &mut Context) -> Result<Type, RuntimeMsg> {
let rhs = self.rhs.execute(context)?;
if let Type::Primitive(rhs) = &rhs {
Ok(Type::Primitive(
rhs.try_not().map_err(RuntimeMsg)?,
))
Ok(Type::Primitive(rhs.try_not().map_err(RuntimeMsg)?))
} else {
Err(RuntimeMsg(format!(
"Cannot apply logical NOT to `{}` ({}): not primitive type",

View file

@ -35,9 +35,7 @@ impl ItemOp for SubtractItemOp {
if let Type::Primitive(lhs) = &lhs {
let rhs = self.rhs.execute(context)?;
if let Type::Primitive(rhs) = &rhs {
Ok(Type::Primitive(
lhs.try_subtract(rhs).map_err(RuntimeMsg)?,
))
Ok(Type::Primitive(lhs.try_subtract(rhs).map_err(RuntimeMsg)?))
} else {
Err(RuntimeMsg(format!(
"Cannot subtract right-hand side `{}` ({}): not primitive type",

View file

@ -6,11 +6,11 @@ use std::net::SocketAddr;
use crate::tokens::Token;
use crate::Context;
use crate::lang::{LanguageDictionary, repeated_tokens, Lookup};
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp};
use crate::lang::{RuntimeError, SyntaxError, RuntimeOp};
use crate::lang::utility::{assert_token, assert_token_raw};
use crate::lang::TypePrimitive;
use crate::lang::{repeated_tokens, LanguageDictionary, Lookup};
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp};
use crate::lang::{RuntimeError, RuntimeOp, SyntaxError};
use crate::processing::general::Type;
use crate::Item;
@ -50,49 +50,61 @@ impl Iterator for MpdQueryStatement {
//let ctx = self.context.as_mut().unwrap();
if self.results.is_none() {
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()) {
Ok(Type::Primitive(a)) => a.as_str(),
Ok(x) => return Some(Err(
RuntimeError {
Ok(x) => {
return Some(Err(RuntimeError {
line: 0,
msg: format!("Cannot use non-primitive `{}` as IP address", x),
op: PseudoOp::from_printable(self),
}
)),
}))
}
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
};
#[cfg(not(feature = "ergonomics"))]
let addr: SocketAddr = match addr_str.parse() {
Ok(a) => a,
Err(e) => return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Cannot convert `{}` to IP Address: {}", addr_str, e),
}))
Err(e) => {
return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Cannot convert `{}` to IP Address: {}", addr_str, e),
}))
}
};
#[cfg(feature = "ergonomics")]
let addr: SocketAddr = if addr_str.starts_with("localhost:") {
let port_str = addr_str.replace("localhost:", "");
let port = match port_str.parse::<u16>() {
Ok(p) => p,
Err(e) => return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Cannot convert `{}` to IP port: {}", port_str, e),
}))
Err(e) => {
return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Cannot convert `{}` to IP port: {}", port_str, e),
}))
}
};
SocketAddr::V4(std::net::SocketAddrV4::new(std::net::Ipv4Addr::LOCALHOST, port))
SocketAddr::V4(std::net::SocketAddrV4::new(
std::net::Ipv4Addr::LOCALHOST,
port,
))
} else if addr_str == "default" {
SocketAddr::V4(std::net::SocketAddrV4::new(std::net::Ipv4Addr::LOCALHOST, 6600))
SocketAddr::V4(std::net::SocketAddrV4::new(
std::net::Ipv4Addr::LOCALHOST,
6600,
))
} else {
match addr_str.parse() {
Ok(a) => a,
Err(e) => return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Cannot convert `{}` to IP Address: {}", addr_str, e),
}))
Err(e) => {
return Some(Err(RuntimeError {
line: 0,
op: PseudoOp::from_printable(self),
msg: format!("Cannot convert `{}` to IP Address: {}", addr_str, e),
}))
}
}
};
// build params
@ -100,21 +112,29 @@ impl Iterator for MpdQueryStatement {
for (term, value) in self.params.iter() {
let static_val = match value.get(self.context.as_mut().unwrap()) {
Ok(Type::Primitive(a)) => a.as_str(),
Ok(x) => return Some(Err(
RuntimeError {
Ok(x) => {
return Some(Err(RuntimeError {
line: 0,
msg: format!("Cannot use non-primitive `{}` MPS query value", x),
op: PseudoOp::from_printable(self),
}
)),
}))
}
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
};
new_params.push((term, static_val));
}
self.results = Some(match self.context.as_mut().unwrap().mpd_database.one_shot_search(addr, new_params) {
Ok(items) => items,
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self)))))
});
self.results = Some(
match self
.context
.as_mut()
.unwrap()
.mpd_database
.one_shot_search(addr, new_params)
{
Ok(items) => items,
Err(e) => return Some(Err(e.with(RuntimeOp(PseudoOp::from_printable(self))))),
},
);
}
let results = self.results.as_mut().unwrap();
results.pop_front().map(Ok)
@ -169,7 +189,10 @@ impl FunctionFactory<MpdQueryStatement> for MpdQueryFunctionFactory {
Ok(MpdQueryStatement {
context: None,
addr: addr_lookup,
params: vec![("any".to_string(), Lookup::Static(Type::Primitive(TypePrimitive::String("".to_owned()))))],
params: vec![(
"any".to_string(),
Lookup::Static(Type::Primitive(TypePrimitive::String("".to_owned()))),
)],
results: None,
})
} else {
@ -182,13 +205,15 @@ impl FunctionFactory<MpdQueryStatement> for MpdQueryFunctionFactory {
_ => None,
},
Token::Name("term".to_string()),
tokens)?;
tokens,
)?;
assert_token_raw(Token::Equals, tokens)?;
let val = Lookup::parse(tokens)?;
Ok(Some((term, val)))
},
Token::Comma
).ingest_all(tokens)?;
Token::Comma,
)
.ingest_all(tokens)?;
Ok(MpdQueryStatement {
context: None,
addr: addr_lookup,
@ -200,7 +225,8 @@ impl FunctionFactory<MpdQueryStatement> for MpdQueryFunctionFactory {
}
#[cfg(feature = "mpd")]
pub type MpdQueryStatementFactory = FunctionStatementFactory<MpdQueryStatement, MpdQueryFunctionFactory>;
pub type MpdQueryStatementFactory =
FunctionStatementFactory<MpdQueryStatement, MpdQueryFunctionFactory>;
#[cfg(feature = "mpd")]
#[inline(always)]

View file

@ -8,9 +8,7 @@ use crate::Item;
use crate::lang::utility::{assert_token, assert_token_raw};
use crate::lang::LanguageDictionary;
use crate::lang::{
FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp,
};
use crate::lang::{FunctionFactory, FunctionStatementFactory, IteratorItem, Op, PseudoOp};
use crate::lang::{RuntimeError, SyntaxError};
#[derive(Debug)]
@ -285,8 +283,7 @@ fn next_comma(tokens: &VecDeque<Token>) -> usize {
tokens.len()
}
pub type RepeatStatementFactory =
FunctionStatementFactory<RepeatStatement, RepeatFunctionFactory>;
pub type RepeatStatementFactory = FunctionStatementFactory<RepeatStatement, RepeatFunctionFactory>;
#[inline(always)]
pub fn repeat_function_factory() -> RepeatStatementFactory {

View file

@ -5,7 +5,7 @@ use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::{assert_name, check_name};
use crate::lang::SyntaxError;
#[cfg(feature = "advanced")]
use crate::lang::{IteratorItem, Op, Sorter, RuntimeMsg};
use crate::lang::{IteratorItem, Op, RuntimeMsg, Sorter};
use crate::lang::{LanguageDictionary, SortStatementFactory, SorterFactory};
use crate::tokens::Token;
#[cfg(feature = "advanced")]

View file

@ -8,7 +8,7 @@ use std::collections::HashMap;
use crate::lang::utility::{assert_name, check_name};
use crate::lang::SyntaxError;
#[cfg(feature = "advanced")]
use crate::lang::{IteratorItem, Op, Sorter, RuntimeMsg};
use crate::lang::{IteratorItem, Op, RuntimeMsg, Sorter};
use crate::lang::{LanguageDictionary, SortStatementFactory, SorterFactory};
use crate::tokens::Token;
#[cfg(feature = "advanced")]

View file

@ -2,8 +2,8 @@ use std::collections::VecDeque;
use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::{IteratorItem, LanguageDictionary, Op};
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
use crate::tokens::Token;
#[derive(Debug, Clone, Default)]

View file

@ -4,8 +4,8 @@ use std::fmt::{Debug, Display, Error, Formatter};
use crate::lang::utility::assert_token;
use crate::lang::{IteratorItem, LanguageDictionary, Op};
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
use crate::tokens::Token;
#[derive(Debug, Clone)]

View file

@ -5,8 +5,8 @@ use rand::{thread_rng, Rng};
use crate::lang::utility::{assert_name, check_name};
use crate::lang::{IteratorItem, LanguageDictionary, Op};
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
use crate::lang::{RuntimeMsg, SyntaxError};
use crate::lang::{SortStatementFactory, Sorter, SorterFactory};
use crate::tokens::Token;
const RNG_LIMIT_BITMASK: usize = 0xffff; // bits to preserve in RNG
@ -90,8 +90,7 @@ impl SorterFactory<ShuffleSorter> for ShuffleSorterFactory {
}
}
pub type ShuffleSorterStatementFactory =
SortStatementFactory<ShuffleSorter, ShuffleSorterFactory>;
pub type ShuffleSorterStatementFactory = SortStatementFactory<ShuffleSorter, ShuffleSorterFactory>;
#[inline(always)]
pub fn shuffle_sort() -> ShuffleSorterStatementFactory {

View file

@ -4,8 +4,7 @@ use std::iter::Iterator;
use crate::lang::utility::assert_token;
use crate::lang::{
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op,
PseudoOp,
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op, PseudoOp,
};
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
use crate::tokens::Token;

View file

@ -4,8 +4,7 @@ use std::iter::Iterator;
use crate::lang::utility::assert_token;
use crate::lang::{
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op,
PseudoOp,
FunctionFactory, FunctionStatementFactory, IteratorItem, LanguageDictionary, Op, PseudoOp,
};
use crate::lang::{RuntimeError, RuntimeMsg, RuntimeOp, SyntaxError};
use crate::tokens::Token;

View file

@ -7,9 +7,7 @@ use crate::Context;
use crate::lang::utility::{assert_token, assert_token_raw, assert_type, check_is_type};
use crate::lang::LanguageDictionary;
use crate::lang::{
BoxedOpFactory, IteratorItem, Op, OpFactory, TypePrimitive, PseudoOp,
};
use crate::lang::{BoxedOpFactory, IteratorItem, Op, OpFactory, PseudoOp, TypePrimitive};
use crate::lang::{RuntimeError, RuntimeOp, SyntaxError};
use crate::processing::general::Type;
@ -75,15 +73,13 @@ impl Iterator for AssignStatement {
Err(e) => return Some(Err(e)),
};
let result = if self.is_declaration {
self
.context
self.context
.as_mut()
.unwrap()
.variables
.declare(&self.variable_name, Type::Op(real))
} else {
self
.context
self.context
.as_mut()
.unwrap()
.variables
@ -107,15 +103,13 @@ impl Iterator for AssignStatement {
} else {
let assign_type = self.assign_type.clone().unwrap();
let result = if self.is_declaration {
self
.context
self.context
.as_mut()
.unwrap()
.variables
.declare(&self.variable_name, Type::Primitive(assign_type))
} else {
self
.context
self.context
.as_mut()
.unwrap()
.variables

View file

@ -3,10 +3,7 @@ use std::path::Path;
use super::Library;
use crate::lang::db::*;
pub fn build_library_from_files<P: AsRef<Path>>(
path: P,
lib: &mut Library,
) -> std::io::Result<()> {
pub fn build_library_from_files<P: AsRef<Path>>(path: P, lib: &mut Library) -> std::io::Result<()> {
//let mut result = Library::new();
lib.read_path(path, 10)?;
Ok(())

View file

@ -71,7 +71,7 @@ impl Library {
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()
}

View file

@ -149,7 +149,9 @@ impl Tags {
pub fn artist(&self, id: u64, genre_id: u64) -> DbArtistItem {
DbArtistItem {
artist_id: id,
name: self.artist_name().unwrap_or_else(|| "Unknown Artist".into()),
name: self
.artist_name()
.unwrap_or_else(|| "Unknown Artist".into()),
genre: genre_id,
}
}

View file

@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
use regex::Regex;
use crate::lang::{TypePrimitive, RuntimeMsg};
use crate::lang::{RuntimeMsg, TypePrimitive};
use crate::Item;
const DEFAULT_REGEX: &str = r"/(?P<artist>[^/]+)/(?P<album>[^/]+)/(?:(?:(?P<disc>\d+)\s+)?(?P<track>\d+)\.?\s+)?(?P<title>[^/]+)\.(?P<format>(?:mp3)|(?:wav)|(?:ogg)|(?:flac)|(?:mp4)|(?:aac))$";
@ -41,7 +41,8 @@ impl Iterator for SortedReadDir {
}
}
self.dir_iter_complete = true;
self.cache.sort_by_key(|b| std::cmp::Reverse(b.path().to_string_lossy().to_lowercase()));
self.cache
.sort_by_key(|b| std::cmp::Reverse(b.path().to_string_lossy().to_lowercase()));
/*self.cache.sort_by(
|a, b| b.path().to_string_lossy().to_lowercase().cmp(
&a.path().to_string_lossy().to_lowercase())
@ -227,7 +228,7 @@ impl FileIter {
if let Some(captures) = captures {
for name in capture_names.flatten() {
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()) {
item.set_field(name, TypePrimitive::parse(value));
}

View file

@ -1,17 +1,17 @@
mod filesystem;
#[cfg(feature = "advanced")]
mod music_analysis;
#[cfg(feature = "mpd")]
mod mpd;
#[cfg(feature = "advanced")]
mod music_analysis;
mod sql;
mod variables;
//pub type OpGetter = dyn FnMut() -> crate::lang::PseudoOp;
pub mod database {
pub use super::sql::{DatabaseQuerier, SQLiteExecutor, QueryResult};
#[cfg(feature = "mpd")]
pub use super::mpd::{MpdQuerier, MpdExecutor};
pub use super::mpd::{MpdExecutor, MpdQuerier};
pub use super::sql::{DatabaseQuerier, QueryResult, SQLiteExecutor};
}
pub mod general {

View file

@ -1,14 +1,14 @@
use core::fmt::Debug;
use std::collections::VecDeque;
use std::net::{SocketAddr, TcpStream};
use std::iter::Iterator;
use std::net::{SocketAddr, TcpStream};
use mpd::Client;
use mpd::{Query, Term, Song};
use mpd::{Query, Song, Term};
use crate::lang::RuntimeMsg;
use crate::Item;
use crate::lang::TypePrimitive;
use crate::Item;
/// Music Player Daemon interface for interacting with it's database
pub trait MpdQuerier: Debug {
@ -16,7 +16,11 @@ pub trait MpdQuerier: Debug {
fn search(&mut self, params: Vec<(&str, String)>) -> Result<VecDeque<Item>, RuntimeMsg>;
fn one_shot_search(&self, addr: SocketAddr, params: Vec<(&str, String)>) -> Result<VecDeque<Item>, RuntimeMsg>;
fn one_shot_search(
&self,
addr: SocketAddr,
params: Vec<(&str, String)>,
) -> Result<VecDeque<Item>, RuntimeMsg>;
}
#[derive(Default, Debug)]
@ -26,7 +30,10 @@ pub struct MpdExecutor {
impl MpdQuerier for MpdExecutor {
fn connect(&mut self, addr: SocketAddr) -> Result<(), RuntimeMsg> {
self.connection = Some(Client::connect(addr).map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?);
self.connection = Some(
Client::connect(addr)
.map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?,
);
Ok(())
}
@ -40,19 +47,31 @@ impl MpdQuerier for MpdExecutor {
for (term, value) in params {
query_mut = query_mut.and(str_to_term(term), value);
}
let songs = self.connection.as_mut().unwrap().search(query_mut, None).map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
let songs = self
.connection
.as_mut()
.unwrap()
.search(query_mut, None)
.map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
Ok(songs.into_iter().map(song_to_item).collect())
}
fn one_shot_search(&self, addr: SocketAddr, params: Vec<(&str, String)>) -> Result<VecDeque<Item>, RuntimeMsg> {
let mut connection = Client::connect(addr).map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?;
fn one_shot_search(
&self,
addr: SocketAddr,
params: Vec<(&str, String)>,
) -> Result<VecDeque<Item>, RuntimeMsg> {
let mut connection = Client::connect(addr)
.map_err(|e| RuntimeMsg(format!("MPD connection error: {}", e)))?;
//let music_dir = connection.music_directory().map_err(|e| RuntimeMsg(format!("MPD command error: {}", e)))?;
let mut query = Query::new();
let mut query_mut = &mut query;
for (term, value) in params {
query_mut = query_mut.and(str_to_term(term), value);
}
let songs = connection.search(query_mut, None).map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
let songs = connection
.search(query_mut, None)
.map_err(|e| RuntimeMsg(format!("MPD search error: {}", e)))?;
Ok(songs.into_iter().map(song_to_item).collect())
}
}
@ -101,6 +120,6 @@ fn str_to_term(s: &str) -> Term<'_> {
"file" => Term::File,
"base" => Term::Base,
"lastmod" => Term::LastMod,
x => Term::Tag(x.into())
x => Term::Tag(x.into()),
}
}

View file

@ -58,12 +58,7 @@ impl std::default::Default for DefaultAnalyzer {
#[cfg(feature = "bliss-audio-symphonia")]
impl DefaultAnalyzer {
fn request_distance(
&mut self,
from: &Item,
to: &Item,
ack: bool,
) -> Result<(), RuntimeMsg> {
fn request_distance(&mut self, from: &Item, to: &Item, ack: bool) -> Result<(), RuntimeMsg> {
let path_from = Self::get_path(from)?;
let path_to = Self::get_path(to)?;
self.requests
@ -89,7 +84,6 @@ impl DefaultAnalyzer {
PATH_FIELD, path
)))
}
} else {
Err(RuntimeMsg(format!(
"Field {} on item is not String, it's {}",
@ -141,7 +135,7 @@ impl MusicAnalyzer for DefaultAnalyzer {
};
}
}
ResponseType::Song { .. } => {},
ResponseType::Song { .. } => {}
ResponseType::UnsupportedSong { path, msg } => {
if path == path_to || path == path_from {
return Err(RuntimeMsg(format!("Bliss error: {}", msg)));
@ -213,7 +207,7 @@ enum ResponseType {
UnsupportedSong {
path: String,
msg: String,
}
},
}
#[cfg(feature = "bliss-audio-symphonia")]
@ -248,11 +242,11 @@ impl CacheThread {
distance,
} => {
self.insert_distance(path1, path2, distance);
},
}
ResponseType::Song { path, song } => {
self.insert_song(path, song);
},
ResponseType::UnsupportedSong { .. } => {},
}
ResponseType::UnsupportedSong { .. } => {}
}
}
}
@ -263,7 +257,8 @@ impl CacheThread {
// avoid using too much memory -- songs are big memory objects
self.song_cache.clear();
}
self.song_cache.insert(path, song_result.map(|x| x.as_ref().to_owned()));
self.song_cache
.insert(path, song_result.map(|x| x.as_ref().to_owned()));
}
fn insert_distance(
@ -309,8 +304,11 @@ impl CacheThread {
} else {
self.insert_song(path2, song);
}
},
ResponseType::UnsupportedSong {path: unsupported_path, ..} => {
}
ResponseType::UnsupportedSong {
path: unsupported_path,
..
} => {
self.song_in_progress.remove(&unsupported_path);
if path == unsupported_path {
return None;
@ -319,10 +317,7 @@ impl CacheThread {
}
}
} else if self.song_cache.contains_key(path) {
let result = self
.song_cache
.get(path)
.and_then(|r| r.clone().ok());
let result = self.song_cache.get(path).and_then(|r| r.clone().ok());
if result.is_none() && auto_add {
self.song_in_progress.insert(path.to_owned());
}
@ -346,11 +341,15 @@ impl CacheThread {
if let Some(result) = self.distance_cache.get(&key) {
if ack {
let result = result.to_owned();
if self.responses.send(ResponseType::Distance {
path1: path1,
path2: path2,
distance: result,
}).is_err() {
if self
.responses
.send(ResponseType::Distance {
path1: path1,
path2: path2,
distance: result,
})
.is_err()
{
return true;
}
}
@ -360,17 +359,26 @@ impl CacheThread {
// also prevents deadlock in self.get_song_option()
// due to waiting on song that isn't being processed yet
// (first call adds it to song_in_progress set, second call just waits)
if ack && self.responses.send(ResponseType::Distance {
path1: path1,
path2: path2,
distance: Ok(0.0),
}).is_err() {
if ack
&& self
.responses
.send(ResponseType::Distance {
path1: path1,
path2: path2,
distance: Ok(0.0),
})
.is_err()
{
return true;
}
} else if !self.distance_in_progress.contains(&key) {
// distance worker uses 3 threads (it's own thread + 1 extra per song) for 2 songs
let available_parallelism =
(std::thread::available_parallelism().ok().map(|x| x.get()).unwrap_or(DEFAULT_PARALLELISM) * 2) / 3;
let available_parallelism = (std::thread::available_parallelism()
.ok()
.map(|x| x.get())
.unwrap_or(DEFAULT_PARALLELISM)
* 2)
/ 3;
let available_parallelism = if available_parallelism != 0 {
available_parallelism - 1
} else {
@ -386,14 +394,17 @@ impl CacheThread {
distance,
} => {
self.insert_distance(path1, path2, distance);
},
}
ResponseType::Song { path: path2, song } => {
self.insert_song(path2.clone(), song.clone());
if self.song_in_progress.len() <= available_parallelism {
break 'inner4;
}
},
ResponseType::UnsupportedSong {path: unsupported_path, ..} => {
}
ResponseType::UnsupportedSong {
path: unsupported_path,
..
} => {
self.song_in_progress.remove(&unsupported_path);
if self.song_in_progress.len() <= available_parallelism {
break 'inner4;
@ -431,11 +442,15 @@ impl CacheThread {
distance.clone(),
);
if path1_2 == key.0 && path2_2 == key.1 {
if self.responses.send(ResponseType::Distance {
path1: path1_2,
path2: path2_2,
distance: distance,
}).is_err() {
if self
.responses
.send(ResponseType::Distance {
path1: path1_2,
path2: path2_2,
distance: distance,
})
.is_err()
{
return true;
}
break 'inner1;
@ -443,13 +458,20 @@ impl CacheThread {
}
ResponseType::Song { path, song } => {
self.insert_song(path, song);
},
ResponseType::UnsupportedSong { path: unsupported_path, msg } => {
}
ResponseType::UnsupportedSong {
path: unsupported_path,
msg,
} => {
self.song_in_progress.remove(&unsupported_path);
if self.responses.send(ResponseType::UnsupportedSong {
path: unsupported_path.clone(),
msg: msg
}).is_err() {
if self
.responses
.send(ResponseType::UnsupportedSong {
path: unsupported_path.clone(),
msg: msg,
})
.is_err()
{
return true;
}
if unsupported_path == key.0 || unsupported_path == key.1 {
@ -476,10 +498,14 @@ impl CacheThread {
} else if !path.contains("://") {
path
} else {
if self.responses.send(ResponseType::UnsupportedSong {
msg: format!("Song path is not a supported URI, it's `{}`", path),
path: path,
}).is_err() {
if self
.responses
.send(ResponseType::UnsupportedSong {
msg: format!("Song path is not a supported URI, it's `{}`", path),
path: path,
})
.is_err()
{
return true;
}
return false;
@ -487,18 +513,25 @@ impl CacheThread {
if let Some(song) = self.song_cache.get(&path) {
if ack {
let song = song.to_owned();
if self.responses.send(ResponseType::Song {
path: path,
song: song.map(Box::new),
}).is_err() {
if self
.responses
.send(ResponseType::Song {
path: path,
song: song.map(Box::new),
})
.is_err()
{
return true;
}
}
} else {
if !self.song_in_progress.contains(&path) {
// every song is roughly 2 threads -- Song::from_path(...) spawns a thread
let available_parallelism =
std::thread::available_parallelism().ok().map(|x| x.get()).unwrap_or(DEFAULT_PARALLELISM) / 2;
let available_parallelism = std::thread::available_parallelism()
.ok()
.map(|x| x.get())
.unwrap_or(DEFAULT_PARALLELISM)
/ 2;
let available_parallelism = if available_parallelism != 0 {
available_parallelism - 1
} else {
@ -520,7 +553,7 @@ impl CacheThread {
if self.song_in_progress.len() <= available_parallelism {
break 'inner2;
}
},
}
ResponseType::UnsupportedSong { path, .. } => {
self.song_in_progress.remove(&path);
if self.song_in_progress.len() <= available_parallelism {
@ -555,22 +588,33 @@ impl CacheThread {
ResponseType::Song { path: path2, song } => {
self.insert_song(path2.clone(), song.clone());
if path2 == path {
if self.responses.send(ResponseType::Song {
path: path,
song: song,
}).is_err() {
if self
.responses
.send(ResponseType::Song {
path: path,
song: song,
})
.is_err()
{
return true;
}
break 'inner3;
}
}
ResponseType::UnsupportedSong { path: unsupported_path, msg } => {
ResponseType::UnsupportedSong {
path: unsupported_path,
msg,
} => {
self.song_in_progress.remove(&unsupported_path);
if unsupported_path == path {
if self.responses.send(ResponseType::UnsupportedSong {
path: unsupported_path,
msg: msg
}).is_err() {
if self
.responses
.send(ResponseType::UnsupportedSong {
path: unsupported_path,
msg: msg,
})
.is_err()
{
return true;
}
break 'inner3;
@ -639,7 +683,11 @@ fn worker_distance(
})
.unwrap_or(());
if new_song2.is_err() {
eprintln!("Song error on `{}`: {}", path2, new_song2.clone().err().unwrap());
eprintln!(
"Song error on `{}`: {}",
path2,
new_song2.clone().err().unwrap()
);
}
new_song2?
};

View file

@ -1,6 +1,6 @@
use core::fmt::Debug;
use std::fmt::Write;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use crate::lang::db::*;
use crate::lang::RuntimeMsg;

View file

@ -3,8 +3,8 @@ use std::fmt::{Debug, Display, Error, Formatter};
use std::collections::HashMap;
use crate::lang::Op;
use crate::lang::TypePrimitive;
use crate::lang::RuntimeMsg;
use crate::lang::TypePrimitive;
use crate::Item;
#[derive(Debug)]

View file

@ -1,7 +1,7 @@
use std::collections::VecDeque;
use super::Token;
use super::ParseError;
use super::Token;
pub trait TokenReader {
fn current_line(&self) -> usize;

View file

@ -1,6 +1,6 @@
//! Integration tests for every syntax feature
use muss_interpreter::tokens::{Token, Tokenizer, ParseError};
use muss_interpreter::tokens::{ParseError, Token, Tokenizer};
use muss_interpreter::*;
use std::collections::VecDeque;
use std::io::Cursor;
@ -823,45 +823,21 @@ fn execute_emptiesop_line() -> Result<(), InterpreterError> {
#[test]
fn execute_nonemptyfilter_line() -> Result<(), InterpreterError> {
execute_single_line(
"files().(??)",
false,
true,
)?;
execute_single_line(
"empties(42).(??)",
true,
true,
)
execute_single_line("files().(??)", false, true)?;
execute_single_line("empties(42).(??)", true, true)
}
#[test]
fn execute_mpdfunction_line() -> Result<(), InterpreterError> {
execute_single_line(
"mpd(`127.0.0.1:6600`, artist=`Bruno Mars`)",
false,
true,
)?;
execute_single_line("mpd(`127.0.0.1:6600`, artist=`Bruno Mars`)", false, true)?;
execute_single_line(
"mpd(`127.0.0.1:6600`, title=`something very long that should match absolutely nothing, probably, hopefully...`)",
true,
true,
)?;
#[cfg(feature = "ergonomics")]
execute_single_line(
"mpd(`localhost:6600`)",
false,
true,
)?;
execute_single_line("mpd(`localhost:6600`)", false, true)?;
#[cfg(feature = "ergonomics")]
execute_single_line(
"mpd(`default`)",
false,
true,
)?;
execute_single_line(
"mpd(`127.0.0.1:6600`)",
false,
true,
)
execute_single_line("mpd(`default`)", false, true)?;
execute_single_line("mpd(`127.0.0.1:6600`)", false, true)
}

View file

@ -33,9 +33,7 @@ fn main() {
for item in runner {
match item {
Ok(music) => {
if let Some(filename) =
music_filename(&music)
{
if let Some(filename) = music_filename(&music) {
playlist.segments.push(MediaSegment {
uri: filename,
title: music_title(&music),
@ -56,9 +54,7 @@ fn main() {
for item in runner {
match item {
Ok(music) => {
if let Some(filename) =
music_filename(&music)
{
if let Some(filename) = music_filename(&music) {
playlist.segments.push(MediaSegment {
uri: filename,
title: music_title(&music),
@ -99,5 +95,3 @@ fn music_filename(item: &Item) -> Option<String> {
None
}
}

View file

@ -1,12 +1,12 @@
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread::JoinHandle;
use muss_interpreter::{Item, InterpreterError};
use muss_interpreter::{InterpreterError, Item};
use super::os_controls::SystemControlWrapper;
use super::player_wrapper::{ControlAction, PlayerServer, PlayerAction};
use super::Player;
use super::player_wrapper::{ControlAction, PlayerAction, PlayerServer};
use super::PlaybackError;
use super::Player;
use super::PlayerError;
/// A controller for a Player running on another thread.
@ -21,7 +21,7 @@ pub struct Controller {
impl Controller {
pub fn create<
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,
) -> Self {
@ -48,7 +48,7 @@ impl Controller {
pub fn create_repl<
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,
) -> Self {
@ -87,14 +87,15 @@ impl Controller {
Ok(())
} else {
Err(PlaybackError {
msg: "Incorrect acknowledgement received for Controller control action"
.into(),
}.into())
msg: "Incorrect acknowledgement received for Controller control action".into(),
}
.into())
}
} else {
Err(PlaybackError {
msg: "Invalid acknowledgement received for Controller control action".into(),
}.into())
}
.into())
}
}
@ -146,7 +147,8 @@ impl Controller {
Ok(x) => Ok(x),
Err(_) => Err(PlaybackError {
msg: "PlayerServer did not exit correctly".into(),
}.into()),
}
.into()),
}
}
@ -208,10 +210,13 @@ impl Controller {
if action == to_send {
break;
} else {
result.push(PlaybackError {
msg: "Incorrect acknowledgement received for Controller control action"
.into(),
}.into());
result.push(
PlaybackError {
msg: "Incorrect acknowledgement received for Controller control action"
.into(),
}
.into(),
);
}
} else if let Err(e) = self.handle_event(msg) {
result.push(e);

View file

@ -1,5 +1,5 @@
use std::fmt::{Debug, Display, Error, Formatter};
use std::convert::From;
use std::fmt::{Debug, Display, Error, Formatter};
#[derive(Debug, Clone)]
pub enum PlayerError {
@ -67,7 +67,7 @@ impl From<PlaybackError> for PlayerError {
#[derive(Debug, Clone)]
pub enum UriError {
Unsupported(String),
Message(String)
Message(String),
}
impl UriError {
@ -81,7 +81,7 @@ impl Display for UriError {
write!(f, "UriError: ")?;
match self {
Self::Unsupported(scheme) => write!(f, "Unsupported URI `{}//`", scheme),
Self::Message(msg) => write!(f, "{}", msg)
Self::Message(msg) => write!(f, "{}", msg),
}
}
}

View file

@ -15,10 +15,10 @@ pub(crate) mod uri;
//mod utility;
pub use controller::Controller;
pub use errors::{PlaybackError, UriError, PlayerError};
pub use player::Player;
pub use errors::{PlaybackError, PlayerError, UriError};
#[cfg(feature = "mpd")]
pub use player::mpd_connection;
pub use player::Player;
//pub use utility::{play_script};
#[cfg(test)]

View file

@ -179,9 +179,7 @@ impl SystemControlWrapper {
fn enqueued(item: Item, dbus_ctrl: &Sender<DbusControl>) {
//println!("Got enqueued item {}", &item.title);
let file_uri = item
.field("filename")
.and_then(|x| x.to_owned().to_str());
let file_uri = item.field("filename").and_then(|x| x.to_owned().to_str());
dbus_ctrl
.send(DbusControl::SetMetadata(Metadata {
length: None,

View file

@ -6,11 +6,11 @@ use rodio::{decoder::Decoder, OutputStream, OutputStreamHandle, Sink};
use m3u8_rs::{MediaPlaylist, MediaSegment};
#[cfg(feature = "mpd")]
use mpd::{Client, Song, error};
use mpd::{error, Client, Song};
use super::uri::Uri;
use muss_interpreter::{Item, InterpreterError};
use muss_interpreter::{InterpreterError, Item};
//use super::PlaybackError;
use super::PlayerError;
@ -20,7 +20,7 @@ use super::UriError;
/// Playback functionality for a script.
/// 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,
sink: Sink,
#[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>>,
}
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> {
let (stream, output_handle) =
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 {
match item {
Ok(music) => {
if let Some(filename) =
music_filename(&music)
{
if let Some(filename) = music_filename(&music) {
//println!("Adding file `{}` to playlist", filename);
playlist.segments.push(MediaSegment {
uri: filename,
@ -215,12 +213,13 @@ impl<I: std::iter::Iterator<Item=Result<Item, InterpreterError>>> Player<I> {
match uri.scheme() {
Some(s) => match &s.to_lowercase() as &str {
"file:" => {
let file = fs::File::open(uri.path()).map_err(PlayerError::from_err_playback)?;
let file =
fs::File::open(uri.path()).map_err(PlayerError::from_err_playback)?;
let stream = io::BufReader::new(file);
let source = Decoder::new(stream).map_err(PlayerError::from_err_playback)?;
self.sink.append(source);
Ok(())
},
}
#[cfg(feature = "mpd")]
"mpd:" => {
if let Some(mpd_client) = &mut self.mpd_connection {
@ -229,13 +228,17 @@ impl<I: std::iter::Iterator<Item=Result<Item, InterpreterError>>> Player<I> {
file: uri.path().to_owned(),
..Default::default()
};
mpd_client.push(song).map_err(PlayerError::from_err_playback)?;
mpd_client
.push(song)
.map_err(PlayerError::from_err_playback)?;
Ok(())
} else {
Err(PlayerError::from_err_playback("Cannot play MPD song: no MPD client connected"))
Err(PlayerError::from_err_playback(
"Cannot play MPD song: no MPD client connected",
))
}
},
scheme => Err(UriError::Unsupported(scheme.to_owned()).into())
}
scheme => Err(UriError::Unsupported(scheme.to_owned()).into()),
},
None => {
//default

View file

@ -2,7 +2,7 @@ use std::sync::mpsc::{Receiver, Sender};
use std::{thread, thread::JoinHandle};
//use muss_interpreter::tokens::TokenReader;
use muss_interpreter::{Item, InterpreterError};
use muss_interpreter::{InterpreterError, Item};
use super::Player;
use super::PlayerError;
@ -11,7 +11,7 @@ use super::PlayerError;
/// This allows for message passing between the player and controller.
///
/// 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>,
control: Receiver<ControlAction>,
event: Sender<PlayerAction>,
@ -19,7 +19,7 @@ pub struct PlayerServer<I: std::iter::Iterator<Item=Result<Item, InterpreterErro
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(
player: Player<I>,
ctrl: Receiver<ControlAction>,

View file

@ -16,14 +16,14 @@ impl<'a> Uri<&'a str> {
Some(end) => {
// proper URI
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('#') {
self.0.get(end+2..frag_start).unwrap()
self.0.get(end + 2..frag_start).unwrap()
} else {
self.0.get(end+2..).unwrap()
self.0.get(end + 2..).unwrap()
}
},
None => self.0
}
None => self.0,
}
}

View file

@ -34,7 +34,9 @@ pub fn parse() -> CliArgs {
pub fn validate(args: &CliArgs) -> Result<(), String> {
if let Some(mpd_addr) = &args.mpd {
let _: std::net::SocketAddr = mpd_addr.parse().map_err(|e| format!("Unrecognized MPS address `{}`: {}", mpd_addr, e))?;
let _: std::net::SocketAddr = mpd_addr
.parse()
.map_err(|e| format!("Unrecognized MPS address `{}`: {}", mpd_addr, e))?;
}
Ok(())
}

View file

@ -81,7 +81,12 @@ fn main() {
// build playback controller
let script_file2 = script_file.clone();
let volume = args.volume;
let mpd = match args.mpd.clone().map(|a| muss_player::mpd_connection(a.parse().unwrap())).transpose() {
let mpd = match args
.mpd
.clone()
.map(|a| muss_player::mpd_connection(a.parse().unwrap()))
.transpose()
{
Ok(mpd) => mpd,
Err(e) => panic!("Abort: Cannot connect to MPD: {}", e),
};

View file

@ -1,27 +1,25 @@
//! Read, Execute, Print Loop functionality
#![allow(clippy::single_match)]
use std::sync::{RwLock};
use std::sync::mpsc::{self, Receiver};
use std::io::{self, Write};
use std::sync::mpsc::{self, Receiver};
use std::sync::RwLock;
use lazy_static::lazy_static;
use console::{Key, Term};
use muss_interpreter::{Interpreter, Debugger, Item, InterpreterEvent, InterpreterError};
use muss_interpreter::lang::TypePrimitive;
use muss_interpreter::{Debugger, Interpreter, InterpreterError, InterpreterEvent, Item};
use muss_player::{Controller, Player};
use super::channel_io::{channel_io, ChannelWriter};
use super::cli::CliArgs;
lazy_static! {
static ref DEBUG_STATE: RwLock<DebugState> = RwLock::new(
DebugState {
debug_flag: DebugFlag::Normal,
verbose: false,
}
);
static ref DEBUG_STATE: RwLock<DebugState> = RwLock::new(DebugState {
debug_flag: DebugFlag::Normal,
verbose: false,
});
}
const TERMINAL_WRITE_ERROR: &str = "Failed to write to terminal output";
@ -55,7 +53,7 @@ struct DebugState {
enum DebugFlag {
Skip,
List,
Normal
Normal,
}
impl ReplState {
@ -80,59 +78,79 @@ impl ReplState {
}
}
fn interpreter_event_callback<T: muss_interpreter::tokens::TokenReader>(_interpreter: &mut Interpreter<'_, T>, event: InterpreterEvent) -> Result<(), InterpreterError> {
fn interpreter_event_callback<T: muss_interpreter::tokens::TokenReader>(
_interpreter: &mut Interpreter<'_, T>,
event: InterpreterEvent,
) -> Result<(), InterpreterError> {
match event {
InterpreterEvent::StatementComplete => {
if let Ok(mut d_state) = DEBUG_STATE.write() {
d_state.debug_flag = DebugFlag::Normal;
}
},
_ => {},
}
_ => {}
}
Ok(())
}
#[inline]
fn item_prompt(terminal: &mut Term, args: &CliArgs) {
write!(terminal, "*I{}", args.prompt)
.expect(TERMINAL_WRITE_ERROR);
write!(terminal, "*I{}", args.prompt).expect(TERMINAL_WRITE_ERROR);
}
fn pretty_print_item(item: &Item, terminal: &mut Term, args: &CliArgs, verbose: bool) {
item_prompt(terminal, args);
if verbose {
writeln!(terminal, "--\\/-- `{}` --\\/--",
item.field("title").unwrap_or(&TypePrimitive::Empty).as_str()
).expect(TERMINAL_WRITE_ERROR);
writeln!(
terminal,
"--\\/-- `{}` --\\/--",
item.field("title")
.unwrap_or(&TypePrimitive::Empty)
.as_str()
)
.expect(TERMINAL_WRITE_ERROR);
let mut fields: Vec<&_> = item.iter().collect();
fields.sort();
for field in fields {
if field != "title" {
writeln!(terminal, " {}: `{}`",
writeln!(
terminal,
" {}: `{}`",
field,
item.field(field).unwrap_or(&TypePrimitive::Empty).as_str()
).expect(TERMINAL_WRITE_ERROR);
)
.expect(TERMINAL_WRITE_ERROR);
}
}
} else {
writeln!(terminal, "`{}` by `{}`",
item.field("title").unwrap_or(&TypePrimitive::Empty).as_str(),
item.field("artist").unwrap_or(&TypePrimitive::Empty).as_str(),
).expect(TERMINAL_WRITE_ERROR);
writeln!(
terminal,
"`{}` by `{}`",
item.field("title")
.unwrap_or(&TypePrimitive::Empty)
.as_str(),
item.field("artist")
.unwrap_or(&TypePrimitive::Empty)
.as_str(),
)
.expect(TERMINAL_WRITE_ERROR);
}
//writeln!(terminal, "I{}----", args.prompt).expect(TERMINAL_WRITE_ERROR);
}
fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
//let items = state.list_rx.try_iter().collect::<Vec<_>>();
let d_state = DEBUG_STATE.read().expect("Failed to get read lock for debug state info").clone();
let d_state = DEBUG_STATE
.read()
.expect("Failed to get read lock for debug state info")
.clone();
for item in state.list_rx.try_iter() {
match item {
Ok(item) => pretty_print_item(&item, &mut state.terminal, args, d_state.verbose),
Err(e) => error_prompt(
muss_player::PlayerError::Playback(
muss_player::PlaybackError::from_err(e)
), args),
muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)),
args,
),
}
}
let flag = d_state.debug_flag;
@ -140,11 +158,13 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
DebugFlag::List => {
while let Ok(item) = state.list_rx.recv() {
match item {
Ok(item) => pretty_print_item(&item, &mut state.terminal, args, d_state.verbose),
Ok(item) => {
pretty_print_item(&item, &mut state.terminal, args, d_state.verbose)
}
Err(e) => error_prompt(
muss_player::PlayerError::Playback(
muss_player::PlaybackError::from_err(e)
), args),
muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)),
args,
),
}
// stop listing if no longer in list mode
let flag = if let Ok(d_state) = DEBUG_STATE.read() {
@ -153,11 +173,11 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
DebugFlag::Normal
};
match flag {
DebugFlag::List => {},
DebugFlag::List => {}
_ => break,
}
}
},
}
_ => {}
}
}
@ -167,18 +187,26 @@ pub fn repl(args: CliArgs) {
term.set_title("muss");
let (writer, reader) = channel_io();
let volume = args.volume;
let mpd = match args.mpd.clone().map(|a| muss_player::mpd_connection(a.parse().unwrap())).transpose() {
let mpd = match args
.mpd
.clone()
.map(|a| muss_player::mpd_connection(a.parse().unwrap()))
.transpose()
{
Ok(mpd) => mpd,
Err(e) => {
eprintln!("Cannot connect to MPD address `{}`: {}", args.mpd.unwrap(), e);
eprintln!(
"Cannot connect to MPD address `{}`: {}",
args.mpd.unwrap(),
e
);
return;
}
};
let (list_tx, list_rx) = mpsc::channel();
let mut state = ReplState::new(writer, term, list_rx);
let player_builder = move || {
let runner = Interpreter::with_stream_and_callback(reader,
&interpreter_event_callback);
let runner = Interpreter::with_stream_and_callback(reader, &interpreter_event_callback);
let debugger = Debugger::new(runner, move |interpretor, item| {
let flag = if let Ok(d_state) = DEBUG_STATE.read() {
d_state.debug_flag
@ -192,7 +220,7 @@ pub fn repl(args: CliArgs) {
// NOTE: recursion occurs here
}
None
},
}
DebugFlag::List => {
if let Some(x) = item {
list_tx.send(x.map_err(|e| e.to_string())).unwrap_or(());
@ -286,7 +314,11 @@ pub fn repl(args: CliArgs) {
}
}
fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut ReplState, mut execute: F) -> ! {
fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(
args: &CliArgs,
state: &mut ReplState,
mut execute: F,
) -> ! {
prompt(state, args);
loop {
match state
@ -296,13 +328,11 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
{
Key::Char(read_c) => {
if state.cursor_rightward_position == 0 {
write!(state.terminal, "{}", read_c)
.expect(TERMINAL_WRITE_ERROR);
write!(state.terminal, "{}", read_c).expect(TERMINAL_WRITE_ERROR);
state.statement_buf.push(read_c);
state.current_line.push(read_c);
} else {
write!(state.terminal, "{}", read_c)
.expect(TERMINAL_WRITE_ERROR);
write!(state.terminal, "{}", read_c).expect(TERMINAL_WRITE_ERROR);
for i in state.current_line.len() - state.cursor_rightward_position
..state.current_line.len()
{
@ -346,21 +376,22 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
}
';' => {
if state.in_literal.is_none() {
state
.terminal
.write_line("")
.expect(TERMINAL_WRITE_ERROR);
state.terminal.write_line("").expect(TERMINAL_WRITE_ERROR);
let statement = state.statement_buf.iter().collect::<String>();
let statement_result = statement.trim();
if !statement_result.starts_with('?') {
state
.writer
.write_all(state.statement_buf.iter().collect::<String>().as_bytes())
.write_all(
state.statement_buf.iter().collect::<String>().as_bytes(),
)
.expect(INTERPRETER_WRITE_ERROR);
execute(state, args);
state.statement_buf.clear();
}
let last_line = state.current_line[0..state.current_line.len() - 1].iter().collect::<String>();
let last_line = state.current_line[0..state.current_line.len() - 1]
.iter()
.collect::<String>();
state.current_line.clear();
if !last_line.is_empty()
&& ((!state.history.is_empty()
@ -415,8 +446,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
.terminal
.move_cursor_left(1)
.expect(TERMINAL_WRITE_ERROR);
write!(state.terminal, " ")
.expect(TERMINAL_WRITE_ERROR);
write!(state.terminal, " ").expect(TERMINAL_WRITE_ERROR);
state
.terminal
.flush()
@ -433,9 +463,9 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
let removed_char = state
.current_line
.remove(state.current_line.len() - state.cursor_rightward_position - 1);
state.statement_buf.remove(
state.statement_buf.len() - state.cursor_rightward_position - 1,
);
state
.statement_buf
.remove(state.statement_buf.len() - state.cursor_rightward_position - 1);
// re-sync unclosed syntax tracking
match removed_char {
'"' | '`' => {
@ -484,9 +514,9 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
let removed_char = state
.current_line
.remove(state.current_line.len() - state.cursor_rightward_position);
state.statement_buf.remove(
state.statement_buf.len() - state.cursor_rightward_position,
);
state
.statement_buf
.remove(state.statement_buf.len() - state.cursor_rightward_position);
// re-sync unclosed syntax tracking
match removed_char {
'"' | '`' => {
@ -528,10 +558,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
}
}
Key::Enter => {
state
.terminal
.write_line("")
.expect(TERMINAL_WRITE_ERROR);
state.terminal.write_line("").expect(TERMINAL_WRITE_ERROR);
let statement = state.statement_buf.iter().collect::<String>();
let statement_result = statement.trim();
if statement_result.starts_with('?') {
@ -578,10 +605,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
1 => {
state.selected_history = 0;
state.line_number -= 1;
state
.terminal
.clear_line()
.expect(TERMINAL_WRITE_ERROR);
state.terminal.clear_line().expect(TERMINAL_WRITE_ERROR);
prompt(state, args);
// clear stale input buffer
state.statement_buf.clear();
@ -589,12 +613,12 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
state.in_literal = None;
state.bracket_depth = 0;
state.curly_depth = 0;
},
0 => {},
}
0 => {}
_ => {
state.selected_history -= 1;
display_history_line(state, args);
},
}
}
}
Key::ArrowLeft => {
@ -624,8 +648,7 @@ fn read_loop<F: FnMut(&mut ReplState, &CliArgs)>(args: &CliArgs, state: &mut Rep
#[inline(always)]
fn prompt(state: &mut ReplState, args: &CliArgs) {
write!(state.terminal, "{: >2}{}", state.line_number, args.prompt)
.expect(TERMINAL_WRITE_ERROR);
write!(state.terminal, "{: >2}{}", state.line_number, args.prompt).expect(TERMINAL_WRITE_ERROR);
state.line_number += 1;
state
.terminal
@ -637,10 +660,7 @@ fn prompt(state: &mut ReplState, args: &CliArgs) {
fn display_history_line(state: &mut ReplState, args: &CliArgs) {
// get historical line
state.line_number -= 1;
state
.terminal
.clear_line()
.expect(TERMINAL_WRITE_ERROR);
state.terminal.clear_line().expect(TERMINAL_WRITE_ERROR);
prompt(state, args);
let new_statement = state.history[state.history.len() - state.selected_history].trim();
state
@ -665,42 +685,62 @@ fn error_prompt(error: muss_player::PlayerError, args: &CliArgs) {
fn repl_commands(command_str: &str, state: &mut ReplState, _args: &CliArgs) {
let words: Vec<&str> = command_str.split(' ').map(|s| s.trim()).collect();
match words[0] {
"?help" => writeln!(state.terminal, "{}", super::help::HELP_STRING).expect(TERMINAL_WRITE_ERROR),
"?function" | "?functions" => writeln!(state.terminal, "{}", super::help::FUNCTIONS).expect(TERMINAL_WRITE_ERROR),
"?filter" | "?filters" => writeln!(state.terminal, "{}", super::help::FILTERS).expect(TERMINAL_WRITE_ERROR),
"?sort" | "?sorter" | "?sorters" => writeln!(state.terminal, "{}", super::help::SORTERS).expect(TERMINAL_WRITE_ERROR),
"?proc" | "?procedure" | "?procedures" => writeln!(state.terminal, "{}", super::help::PROCEDURES).expect(TERMINAL_WRITE_ERROR),
"?help" => {
writeln!(state.terminal, "{}", super::help::HELP_STRING).expect(TERMINAL_WRITE_ERROR)
}
"?function" | "?functions" => {
writeln!(state.terminal, "{}", super::help::FUNCTIONS).expect(TERMINAL_WRITE_ERROR)
}
"?filter" | "?filters" => {
writeln!(state.terminal, "{}", super::help::FILTERS).expect(TERMINAL_WRITE_ERROR)
}
"?sort" | "?sorter" | "?sorters" => {
writeln!(state.terminal, "{}", super::help::SORTERS).expect(TERMINAL_WRITE_ERROR)
}
"?proc" | "?procedure" | "?procedures" => {
writeln!(state.terminal, "{}", super::help::PROCEDURES).expect(TERMINAL_WRITE_ERROR)
}
"?list" => {
{
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
let mut debug_state = DEBUG_STATE
.write()
.expect("Failed to get write lock for debug state");
debug_state.debug_flag = DebugFlag::List;
}
writeln!(state.terminal, "Listing upcoming items").expect(TERMINAL_WRITE_ERROR);
},
}
"?skip" => {
{
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
let mut debug_state = DEBUG_STATE
.write()
.expect("Failed to get write lock for debug state");
debug_state.debug_flag = DebugFlag::Skip;
}
writeln!(state.terminal, "Skipping upcoming items").expect(TERMINAL_WRITE_ERROR);
},
}
"?normal" => {
{
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
let mut debug_state = DEBUG_STATE
.write()
.expect("Failed to get write lock for debug state");
debug_state.debug_flag = DebugFlag::Normal;
}
writeln!(state.terminal, "Resuming normal operation").expect(TERMINAL_WRITE_ERROR);
},
}
"?verbose" => {
let verbose = {
let mut debug_state = DEBUG_STATE.write().expect("Failed to get write lock for debug state");
let mut debug_state = DEBUG_STATE
.write()
.expect("Failed to get write lock for debug state");
debug_state.verbose = !debug_state.verbose;
debug_state.verbose
};
writeln!(state.terminal, "Verbosed toggled to {}", verbose).expect(TERMINAL_WRITE_ERROR);
},
"?commands" => writeln!(state.terminal, "{}", super::help::REPL_COMMANDS).expect(TERMINAL_WRITE_ERROR),
writeln!(state.terminal, "Verbosed toggled to {}", verbose)
.expect(TERMINAL_WRITE_ERROR);
}
"?commands" => {
writeln!(state.terminal, "{}", super::help::REPL_COMMANDS).expect(TERMINAL_WRITE_ERROR)
}
_ => writeln!(state.terminal, "Unknown command, try ?help").expect(TERMINAL_WRITE_ERROR),
}
}