use std::fmt;
use bimap::BiMap;
use bytes::Bytes;
use termcolor::{Color, ColorSpec, WriteColor};
use crate::{
commands,
commands::{Commands, Responses},
packet,
};
pub struct Decoder {
quiet: bool,
w: Box<dyn WriteColor + Send + Sync>,
name_map: Option<BiMap<String, usize>>,
}
impl Decoder {
pub fn new(
w: Box<dyn WriteColor + Send + Sync>,
quiet: bool,
name_map: Option<BiMap<String, usize>>,
) -> Self {
Decoder { quiet, w, name_map }
}
pub fn set_name_map<'a>(&mut self, it: impl Iterator<Item = (&'a str, u16)>) {
let mut map = BiMap::new();
for (k, v) in it {
map.insert(k.to_string(), v as usize);
}
self.name_map.replace(map);
}
pub fn feed_sent(&mut self, frame: &Bytes) {
if let Ok(frame) = packet::unframe(frame.clone()) {
match commands::Commands::from_bytes(frame.clone()) {
Ok(cmd) => {
if matches!(
cmd,
commands::Commands::ReadFloats { .. } | commands::Commands::Read { .. }
) && self.quiet
{
return;
}
let _ = self.print_frame(true, &frame);
let _ = self.print_command(cmd);
}
Err(err) => {
let _ = self.print_frame(true, &frame);
let _ = self.print_error(err);
}
};
}
}
pub fn feed_recv(&mut self, frame: &Bytes) {
if let Ok(frame) = packet::unframe(frame.clone()) {
match commands::Responses::from_bytes(frame.clone()) {
Ok(cmd) => {
if matches!(
cmd,
commands::Responses::FloatData(_) | commands::Responses::Read { .. }
) && self.quiet
{
return;
}
let _ = self.print_frame(false, &frame);
let _ = self.print_response(cmd);
}
Err(err) => {
let _ = self.print_frame(false, &frame);
let _ = self.print_error(err);
}
}
}
}
fn print_frame(&mut self, sent: bool, frame: &Bytes) -> std::io::Result<()> {
let _ = self.print_direction(sent);
let _ = self
.w
.set_color(ColorSpec::new().set_fg(Some(Color::White)).set_dimmed(true));
writeln!(self.w, "{:02x?}", frame.as_ref())?;
Ok(())
}
fn print_command(&mut self, cmd: Commands) -> std::io::Result<()> {
let _ = self.print_direction(true);
let _ = self.w.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)));
write!(self.w, "{:02x?} ", cmd)?;
let _ = self.maybe_print_addr(&ParsedMessage::Request(cmd));
Ok(())
}
fn print_response(&mut self, cmd: Responses) -> std::io::Result<()> {
let _ = self.print_direction(false);
let _ = self
.w
.set_color(ColorSpec::new().set_fg(Some(Color::Green)));
write!(self.w, "{:02x?}", cmd)?;
let _ = self.maybe_print_addr(&ParsedMessage::Response(cmd));
Ok(())
}
fn print_direction(&mut self, sent: bool) -> std::io::Result<()> {
let direction = if sent {
let _ = self.w.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)));
"Sent: "
} else {
let _ = self.w.set_color(ColorSpec::new().set_fg(Some(Color::Blue)));
"Recv: "
};
write!(self.w, "{}", direction)?;
Ok(())
}
fn print_error<T: fmt::Debug>(&mut self, err: T) -> std::io::Result<()> {
let _ = self.w.set_color(ColorSpec::new().set_fg(Some(Color::Red)));
writeln!(self.w, "Decode error: {:?}", err)?;
Ok(())
}
fn maybe_print_addr(&mut self, cmd: &ParsedMessage) -> std::io::Result<()> {
let addr = match *cmd {
ParsedMessage::Request(Commands::ReadFloats { addr, .. }) => addr,
ParsedMessage::Request(Commands::Write { addr, .. }) => addr.val as _,
ParsedMessage::Request(Commands::WriteBiquad { addr, .. }) => addr.val as _,
ParsedMessage::Request(Commands::WriteBiquadBypass { addr, .. }) => addr.val as _,
ParsedMessage::Request(Commands::Read { addr, .. }) => addr.val as _,
ParsedMessage::Request(Commands::SwitchMux { addr, .. }) => addr.val as _,
_ => {
return writeln!(self.w);
}
};
let _ = self
.w
.set_color(ColorSpec::new().set_fg(Some(Color::Magenta)));
let name = self
.resolve_addr(addr)
.unwrap_or_else(|| "<unknown>".to_string());
writeln!(self.w, "(0x{:02x?} | {:?}) <> {}", addr, addr, name,)?;
Ok(())
}
fn resolve_addr(&self, addr: u16) -> Option<String> {
Some(
self.name_map
.as_ref()?
.get_by_right(&(addr as usize))?
.clone(),
)
}
}
pub enum ParsedMessage {
Request(Commands),
Response(Responses),
}
#[cfg(test)]
mod test {
use termcolor::{ColorChoice, StandardStream};
use super::*;
#[test]
fn test_print() {
let writer = Box::new(StandardStream::stderr(ColorChoice::Always));
let mut d = Decoder {
w: writer,
quiet: false,
name_map: None,
};
d.feed_sent(&Bytes::from_static(&[0x05, 0x14, 0x00, 0x46, 0x04, 0x63]));
d.feed_recv(&Bytes::from_static(&[
0x05, 0x14, 0x00, 0x46, 0x00, 0x00, 0x00,
]));
d.w.reset().unwrap();
}
}