Make verbose item print out configurable by field

This commit is contained in:
NGnius (Graham) 2023-10-16 21:37:08 -04:00
parent 507add66b8
commit d0c7bcda58
3 changed files with 190 additions and 64 deletions

View file

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

View file

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

View file

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