Make verbose item print out configurable by field
This commit is contained in:
parent
507add66b8
commit
d0c7bcda58
3 changed files with 190 additions and 64 deletions
|
@ -53,6 +53,18 @@ impl TypePrimitive {
|
|||
}
|
||||
}
|
||||
|
||||
/// Pretty-print the type info, without the value
|
||||
pub fn type_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::String(_) => "String",
|
||||
Self::Int(_) => "Int",
|
||||
Self::UInt(_) => "UInt",
|
||||
Self::Float(_) => "Float",
|
||||
Self::Bool(_) => "Bool",
|
||||
Self::Empty => "Empty",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_u64(self) -> Option<u64> {
|
||||
match self {
|
||||
Self::UInt(x) => Some(x),
|
||||
|
|
|
@ -3,11 +3,15 @@ use muss_interpreter::Item;
|
|||
use muss_player::EventTap;
|
||||
use muss_player::{PlaybackAction, PlayerAction, ControlAction};
|
||||
|
||||
use crate::repl::TERMINAL_WRITE_ERROR;
|
||||
|
||||
use std::io::Write;
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct InterpreterDebugState {
|
||||
pub debug_flag: InterpreterDebugFlag,
|
||||
pub verbose: bool,
|
||||
pub verbose: Verbosity,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
|
@ -17,6 +21,134 @@ pub enum InterpreterDebugFlag {
|
|||
Normal,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Verbosity {
|
||||
field_handlers: Vec<VerboseField>,
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum VerboseField {
|
||||
ShowFieldValue(String),
|
||||
ShowFieldType(String),
|
||||
ShowFieldTypeValue(String),
|
||||
}
|
||||
|
||||
impl VerboseField {
|
||||
fn print_field(&self, item: &Item, terminal: &mut console::Term, args: &crate::cli::CliArgs) -> bool {
|
||||
match self {
|
||||
Self::ShowFieldValue(f) => {
|
||||
if let Some(field) = item.field(f) {
|
||||
let field_str = field.as_str();
|
||||
let max_len = terminal.size().1 as usize - (args.prompt.len() + f.len() + 6);
|
||||
if field_str.len() >= max_len {
|
||||
write!(
|
||||
terminal,
|
||||
" {}{}: `{}[...]`",
|
||||
args.prompt,
|
||||
f,
|
||||
&field_str[..max_len - 5]
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
} else {
|
||||
write!(terminal, " {}{}: `{}`", args.prompt, f, field_str).expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
Self::ShowFieldType(f) => {
|
||||
if let Some(field) = item.field(f) {
|
||||
let field_str = field.type_str();
|
||||
write!(terminal, " {}{}: {}", args.prompt, f, field_str).expect(TERMINAL_WRITE_ERROR);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
},
|
||||
Self::ShowFieldTypeValue(f) => {
|
||||
if let Some(field) = item.field(f) {
|
||||
let field_str = format!("{}", field);
|
||||
let max_len = terminal.size().1 as usize - (args.prompt.len() + f.len() + 4);
|
||||
if field_str.len() >= max_len {
|
||||
write!(
|
||||
terminal,
|
||||
" {}{}: {}[...]]",
|
||||
args.prompt,
|
||||
f,
|
||||
&field_str[..max_len - 5]
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
} else {
|
||||
write!(terminal, " {}{}: {}", args.prompt, f, field_str).expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Verbosity {
|
||||
pub fn print_item(&self, item: &Item, terminal: &mut console::Term, args: &crate::cli::CliArgs) {
|
||||
Self::item_prompt(terminal, args);
|
||||
if self.debug {
|
||||
writeln!(terminal, "--\\/-- \\/ --\\/--").expect(TERMINAL_WRITE_ERROR);
|
||||
if self.field_handlers.is_empty() {
|
||||
let mut fields: Vec<_> = item.iter().collect();
|
||||
fields.sort();
|
||||
for field in fields {
|
||||
VerboseField::ShowFieldValue(field.to_owned()).print_field(item, terminal, args);
|
||||
writeln!(terminal, "").expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
} else {
|
||||
for vf in &self.field_handlers {
|
||||
let has_printed = vf.print_field(item, terminal, args);
|
||||
if has_printed {
|
||||
writeln!(terminal, "").expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writeln!(
|
||||
terminal,
|
||||
"`{}` by `{}`",
|
||||
item.field("title")
|
||||
.unwrap_or(&muss_interpreter::lang::TypePrimitive::Empty)
|
||||
.as_str(),
|
||||
item.field("artist")
|
||||
.unwrap_or(&muss_interpreter::lang::TypePrimitive::Empty)
|
||||
.as_str(),
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verbose_mut(&mut self) -> &'_ mut bool {
|
||||
&mut self.debug
|
||||
}
|
||||
|
||||
pub fn verbose(&mut self) -> &'_ bool {
|
||||
&self.debug
|
||||
}
|
||||
|
||||
pub fn handlers(&mut self, handlers: Vec<VerboseField>) {
|
||||
self.field_handlers = handlers;
|
||||
}
|
||||
|
||||
pub fn clear_handlers(&mut self) {
|
||||
self.field_handlers.clear();
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn item_prompt(terminal: &mut console::Term, args: &crate::cli::CliArgs) {
|
||||
write!(terminal, "I{}", args.prompt).expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DebugState {
|
||||
pub now_playing: std::sync::Arc<std::sync::RwLock<Option<Item>>>,
|
||||
|
@ -31,7 +163,10 @@ impl DebugState {
|
|||
control_tx: std::sync::Arc::new(std::sync::Mutex::new(None)),
|
||||
interpreter: std::sync::Arc::new(std::sync::RwLock::new(InterpreterDebugState {
|
||||
debug_flag: InterpreterDebugFlag::Normal,
|
||||
verbose: false,
|
||||
verbose: Verbosity {
|
||||
field_handlers: Vec::new(),
|
||||
debug: false,
|
||||
},
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
|
103
src/repl.rs
103
src/repl.rs
|
@ -5,7 +5,6 @@ use std::sync::mpsc::{self, Receiver};
|
|||
|
||||
use console::{Key, Term};
|
||||
|
||||
use muss_interpreter::lang::TypePrimitive;
|
||||
use muss_interpreter::{Debugger, Interpreter, InterpreterError, InterpreterEvent, Item};
|
||||
use muss_player::{Controller, Player};
|
||||
|
||||
|
@ -75,57 +74,6 @@ fn interpreter_event_callback<'a, T: muss_interpreter::tokens::TokenReader>(
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn item_prompt(terminal: &mut Term, args: &CliArgs) {
|
||||
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);
|
||||
let mut fields: Vec<&_> = item.iter().collect();
|
||||
fields.sort();
|
||||
for field in fields {
|
||||
if field != "title" {
|
||||
let field_str = item.field(field).unwrap_or(&TypePrimitive::Empty).as_str();
|
||||
let max_len = terminal.size().1 as usize - (args.prompt.len() + field.len() + 8);
|
||||
if field_str.len() >= max_len {
|
||||
writeln!(
|
||||
terminal,
|
||||
" {}: `{}[...]`",
|
||||
field,
|
||||
&field_str[..max_len - 5]
|
||||
)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
} else {
|
||||
writeln!(terminal, " {}: `{}`", field, field_str).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, "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 = state.controller_debug.interpreter
|
||||
|
@ -134,7 +82,7 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
|
|||
.clone();
|
||||
for item in state.list_rx.try_iter() {
|
||||
match item {
|
||||
Ok(item) => pretty_print_item(&item, &mut state.terminal, args, d_state.verbose),
|
||||
Ok(item) => d_state.verbose.print_item(&item, &mut state.terminal, args),
|
||||
Err(e) => error_prompt(
|
||||
muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)),
|
||||
args,
|
||||
|
@ -148,8 +96,11 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
|
|||
while let Ok(item) = state.list_rx.recv() {
|
||||
match item {
|
||||
Ok(item) => {
|
||||
let verbose = state.controller_debug.interpreter.read().map(|x| x.verbose).unwrap_or(false);
|
||||
pretty_print_item(&item, &mut state.terminal, args, verbose)
|
||||
state.controller_debug.interpreter
|
||||
.read()
|
||||
.expect("Failed to get read lock for debug state info")
|
||||
.verbose
|
||||
.print_item(&item, &mut state.terminal, args)
|
||||
}
|
||||
Err(e) => error_prompt(
|
||||
muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)),
|
||||
|
@ -680,7 +631,7 @@ 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();
|
||||
let mut 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)
|
||||
|
@ -729,10 +680,38 @@ fn repl_commands(command_str: &str, state: &mut ReplState, args: &CliArgs) {
|
|||
let mut debug_state = state.controller_debug.interpreter
|
||||
.write()
|
||||
.expect("Failed to get write lock for debug state");
|
||||
debug_state.verbose = !debug_state.verbose;
|
||||
debug_state.verbose
|
||||
if *debug_state.verbose.verbose() && words.len() == 1 {
|
||||
*debug_state.verbose.verbose_mut() = false;
|
||||
} else if !*debug_state.verbose.verbose() && words.len() == 1 {
|
||||
*debug_state.verbose.verbose_mut() = true;
|
||||
debug_state.verbose.clear_handlers();
|
||||
} else {
|
||||
*debug_state.verbose.verbose_mut() = true;
|
||||
debug_state.verbose.handlers(
|
||||
words.split_off(1)
|
||||
.into_iter()
|
||||
.filter_map(|arg| {
|
||||
const HANDLER_SPLIT_CHAR: char = ':';
|
||||
if arg.contains(HANDLER_SPLIT_CHAR) {
|
||||
let mut iter = arg.split(HANDLER_SPLIT_CHAR);
|
||||
let handler = iter.next().unwrap();
|
||||
let field = if let Some(f) = iter.next() { f } else { return None };
|
||||
match handler {
|
||||
"v" | "value" => Some(crate::debug_state::VerboseField::ShowFieldValue(field.to_owned())),
|
||||
"t" | "type" => Some(crate::debug_state::VerboseField::ShowFieldType(field.to_owned())),
|
||||
"vt" | "valuetype" => Some(crate::debug_state::VerboseField::ShowFieldTypeValue(field.to_owned())),
|
||||
_ => None
|
||||
}
|
||||
} else {
|
||||
Some(crate::debug_state::VerboseField::ShowFieldValue(arg.to_owned()))
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
);
|
||||
}
|
||||
*debug_state.verbose.verbose()
|
||||
};
|
||||
writeln!(state.terminal, "Verbosed toggled to {}", verbose)
|
||||
writeln!(state.terminal, "Verbose toggled to {}", verbose)
|
||||
.expect(TERMINAL_WRITE_ERROR);
|
||||
}
|
||||
"?commands" => {
|
||||
|
@ -740,11 +719,11 @@ fn repl_commands(command_str: &str, state: &mut ReplState, args: &CliArgs) {
|
|||
}
|
||||
"?now" => {
|
||||
if let Some(item) = crate::playlists::get_current_item(state) {
|
||||
let verbose = state.controller_debug.interpreter
|
||||
state.controller_debug.interpreter
|
||||
.read()
|
||||
.expect("Failed to get read lock for debug state")
|
||||
.verbose;
|
||||
pretty_print_item(&item, &mut state.terminal, args, verbose);
|
||||
.verbose
|
||||
.print_item(&item, &mut state.terminal, args);
|
||||
} else {
|
||||
writeln!(state.terminal, "Nothing playing").expect(TERMINAL_WRITE_ERROR)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue