parent
b63b72ed90
commit
eee4c624f9
@ -0,0 +1,15 @@
|
||||
#[cfg(feature = "ui_debug")]
|
||||
mod maybe_trace {
|
||||
use crate::trace::Trace;
|
||||
|
||||
pub trait MaybeTrace: Trace {}
|
||||
impl<T> MaybeTrace for T where T: Trace {}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "ui_debug"))]
|
||||
mod maybe_trace {
|
||||
pub trait MaybeTrace {}
|
||||
impl<T> MaybeTrace for T {}
|
||||
}
|
||||
|
||||
pub use maybe_trace::MaybeTrace;
|
@ -1,91 +1,212 @@
|
||||
/// Visitor passed into `Trace` types.
|
||||
use crate::strutil::format_i64;
|
||||
|
||||
pub trait Tracer {
|
||||
fn child(&mut self, key: &str, value: &dyn Trace);
|
||||
fn int(&mut self, key: &str, i: i64);
|
||||
fn string(&mut self, key: &str, s: &str);
|
||||
fn bool(&mut self, key: &str, b: bool);
|
||||
|
||||
fn in_child(&mut self, key: &str, block: &dyn Fn(&mut dyn Tracer));
|
||||
fn in_list(&mut self, key: &str, block: &dyn Fn(&mut dyn ListTracer));
|
||||
|
||||
fn component(&mut self, name: &str) {
|
||||
self.string("component", name);
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ListTracer {
|
||||
fn child(&mut self, value: &dyn Trace);
|
||||
fn int(&mut self, i: i64);
|
||||
fn bytes(&mut self, b: &[u8]);
|
||||
fn string(&mut self, s: &str);
|
||||
fn symbol(&mut self, name: &str);
|
||||
fn open(&mut self, name: &str);
|
||||
fn field(&mut self, name: &str, value: &dyn Trace);
|
||||
fn close(&mut self);
|
||||
fn bool(&mut self, b: bool);
|
||||
|
||||
fn in_child(&mut self, block: &dyn Fn(&mut dyn Tracer));
|
||||
fn in_list(&mut self, block: &dyn Fn(&mut dyn ListTracer));
|
||||
}
|
||||
|
||||
/// Value that can describe own structure and data using the `Tracer` interface.
|
||||
pub trait Trace {
|
||||
fn trace(&self, d: &mut dyn Tracer);
|
||||
/// Generic tracer based on a TraceWriter.
|
||||
pub struct JsonTracer<F: FnMut(&str)> {
|
||||
write_fn: F,
|
||||
write_buf: [u8; 32],
|
||||
buf_pos: usize,
|
||||
first: bool,
|
||||
}
|
||||
|
||||
impl Trace for &[u8] {
|
||||
fn trace(&self, t: &mut dyn Tracer) {
|
||||
t.bytes(self);
|
||||
impl<F: FnMut(&str)> JsonTracer<F> {
|
||||
pub fn new(write_fn: F) -> Self {
|
||||
Self {
|
||||
write_fn,
|
||||
write_buf: [0; 32],
|
||||
buf_pos: 0,
|
||||
first: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Trace for &[u8; N] {
|
||||
fn trace(&self, t: &mut dyn Tracer) {
|
||||
t.bytes(&self[..])
|
||||
fn maybe_comma(&mut self) {
|
||||
if !self.first {
|
||||
(self.write_fn)(", ");
|
||||
}
|
||||
self.first = false;
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace for &str {
|
||||
fn trace(&self, t: &mut dyn Tracer) {
|
||||
t.string(self);
|
||||
fn write_int(&mut self, i: i64) {
|
||||
let s = format_i64(i, &mut self.write_buf).unwrap_or("\"###NUMERR###\"");
|
||||
(self.write_fn)(s);
|
||||
}
|
||||
}
|
||||
|
||||
impl Trace for usize {
|
||||
fn trace(&self, t: &mut dyn Tracer) {
|
||||
t.int(*self as i64);
|
||||
// SAFETY: there must be a valid utf8 string in write_buf[..buf_pos]
|
||||
unsafe fn flush_buf(&mut self) {
|
||||
if self.buf_pos == 0 {
|
||||
return;
|
||||
}
|
||||
let substr = &self.write_buf[..self.buf_pos];
|
||||
let s = unsafe { core::str::from_utf8_unchecked(substr) };
|
||||
(self.write_fn)(s);
|
||||
self.buf_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Trace for Option<T>
|
||||
where
|
||||
T: Trace,
|
||||
{
|
||||
fn trace(&self, d: &mut dyn Tracer) {
|
||||
match self {
|
||||
Some(v) => v.trace(d),
|
||||
None => d.symbol("None"),
|
||||
// SAFETY: there must be a valid utf8 string in write_buf[..buf_pos].
|
||||
// Given that there is valid utf8 at start, there will be valid utf8 when done.
|
||||
unsafe fn push_or_flush(&mut self, ch: char) {
|
||||
let size = ch.len_utf8();
|
||||
if self.buf_pos + size > self.write_buf.len() {
|
||||
unsafe { self.flush_buf() };
|
||||
}
|
||||
ch.encode_utf8(&mut self.write_buf[self.buf_pos..]);
|
||||
self.buf_pos += size;
|
||||
}
|
||||
|
||||
fn write_str_quoted(&mut self, s: &str) {
|
||||
self.buf_pos = 0;
|
||||
// SAFETY: we clear the writebuf by resetting buf_pos to 0, so its contents
|
||||
// are only affected by push_or_flush, which keeps contents of write_buf
|
||||
// correct.
|
||||
unsafe {
|
||||
self.push_or_flush('"');
|
||||
for mut ch in s.chars() {
|
||||
if ch == '\\' || ch == '"' {
|
||||
self.push_or_flush('\\');
|
||||
} else if ch == '\n' {
|
||||
self.push_or_flush('\\');
|
||||
ch = 'n';
|
||||
}
|
||||
self.push_or_flush(ch);
|
||||
}
|
||||
self.push_or_flush('"');
|
||||
self.flush_buf();
|
||||
}
|
||||
}
|
||||
|
||||
fn key(&mut self, key: &str) {
|
||||
self.maybe_comma();
|
||||
self.write_str_quoted(key);
|
||||
(self.write_fn)(": ");
|
||||
}
|
||||
|
||||
pub fn root(&mut self, block: &dyn Fn(&mut dyn Tracer)) {
|
||||
(self.write_fn)("{");
|
||||
block(self);
|
||||
(self.write_fn)("}");
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
impl<F: FnMut(&str)> ListTracer for JsonTracer<F> {
|
||||
fn child(&mut self, value: &dyn Trace) {
|
||||
ListTracer::in_child(self, &mut |t| value.trace(t));
|
||||
}
|
||||
|
||||
impl Tracer for Vec<u8> {
|
||||
fn int(&mut self, i: i64) {
|
||||
self.string(&i.to_string());
|
||||
}
|
||||
fn int(&mut self, i: i64) {
|
||||
self.maybe_comma();
|
||||
self.write_int(i);
|
||||
}
|
||||
|
||||
fn bytes(&mut self, b: &[u8]) {
|
||||
self.extend(b)
|
||||
}
|
||||
fn string(&mut self, s: &str) {
|
||||
self.maybe_comma();
|
||||
self.write_str_quoted(s);
|
||||
}
|
||||
|
||||
fn string(&mut self, s: &str) {
|
||||
self.extend(s.as_bytes())
|
||||
}
|
||||
fn bool(&mut self, b: bool) {
|
||||
self.maybe_comma();
|
||||
(self.write_fn)(if b { "true" } else { "false" });
|
||||
}
|
||||
|
||||
fn symbol(&mut self, name: &str) {
|
||||
self.extend(name.as_bytes())
|
||||
}
|
||||
fn in_child(&mut self, block: &dyn Fn(&mut dyn Tracer)) {
|
||||
self.maybe_comma();
|
||||
self.first = true;
|
||||
(self.write_fn)("{");
|
||||
block(self);
|
||||
(self.write_fn)("}");
|
||||
self.first = false;
|
||||
}
|
||||
|
||||
fn open(&mut self, name: &str) {
|
||||
self.extend(b"<");
|
||||
self.extend(name.as_bytes());
|
||||
self.extend(b" ");
|
||||
}
|
||||
fn in_list(&mut self, block: &dyn Fn(&mut dyn ListTracer)) {
|
||||
self.maybe_comma();
|
||||
self.first = true;
|
||||
(self.write_fn)("[");
|
||||
block(self);
|
||||
(self.write_fn)("]");
|
||||
self.first = false;
|
||||
}
|
||||
}
|
||||
|
||||
fn field(&mut self, name: &str, value: &dyn Trace) {
|
||||
self.extend(name.as_bytes());
|
||||
self.extend(b":");
|
||||
value.trace(self);
|
||||
self.extend(b" ");
|
||||
}
|
||||
impl<F: FnMut(&str)> Tracer for JsonTracer<F> {
|
||||
fn child(&mut self, key: &str, value: &dyn Trace) {
|
||||
Tracer::in_child(self, key, &|t| value.trace(t));
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
self.extend(b">")
|
||||
}
|
||||
fn int(&mut self, key: &str, i: i64) {
|
||||
self.key(key);
|
||||
self.write_int(i);
|
||||
}
|
||||
|
||||
fn string(&mut self, key: &str, s: &str) {
|
||||
self.key(key);
|
||||
self.write_str_quoted(s);
|
||||
}
|
||||
|
||||
fn bool(&mut self, key: &str, b: bool) {
|
||||
self.key(key);
|
||||
(self.write_fn)(if b { "true" } else { "false" });
|
||||
}
|
||||
|
||||
fn in_child(&mut self, key: &str, block: &dyn Fn(&mut dyn Tracer)) {
|
||||
self.key(key);
|
||||
(self.write_fn)("{");
|
||||
self.first = true;
|
||||
block(self);
|
||||
(self.write_fn)("}");
|
||||
self.first = false;
|
||||
}
|
||||
|
||||
fn in_list(&mut self, key: &str, block: &dyn Fn(&mut dyn ListTracer)) {
|
||||
self.key(key);
|
||||
(self.write_fn)("[");
|
||||
self.first = true;
|
||||
block(self);
|
||||
(self.write_fn)("]");
|
||||
self.first = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// Value that can describe own structure and data using the `Tracer`
|
||||
/// interface.
|
||||
pub trait Trace {
|
||||
fn trace(&self, t: &mut dyn Tracer);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod tests {
|
||||
extern crate serde_json;
|
||||
use serde_json::Value;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub fn trace(val: &impl Trace) -> Value {
|
||||
let mut buf = Vec::new();
|
||||
let mut tracer = JsonTracer::new(|text| buf.extend_from_slice(text.as_bytes()));
|
||||
tracer.root(&|t| val.trace(t));
|
||||
let s = String::from_utf8(buf).unwrap();
|
||||
//crate::micropython::print::print(s.as_str());
|
||||
s.parse().unwrap()
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in new issue