cbor-bin 0.1.1

A command line tool for working with CBOR
use cbor_data::{Cbor, ItemKind, TaggedItem, Visitor};
use std::fmt::{Display, Formatter};

pub struct BriefDisplay<'a> {
    pub cbor: &'a Cbor,
    pub max_depth: usize,
    pub array_length: usize,
    pub censored_properties: &'a [String],
    pub text_length: usize,
}

impl<'a> Display for BriefDisplay<'a> {
    fn fmt(&self, fmt: &mut Formatter<'_>) -> std::fmt::Result {
        self.cbor.visit(&mut BriefDisplayVisitor {
            fmt,
            depth: 0,
            max_depth: self.max_depth,
            array_length: self.array_length as u64,
            censored_properties: self.censored_properties,
            text_length: self.text_length,
        })
    }
}

struct BriefDisplayVisitor<'a, 'b> {
    fmt: &'a mut Formatter<'b>,
    depth: usize,
    max_depth: usize,
    array_length: u64,
    censored_properties: &'a [String],
    text_length: usize,
}

impl<'a, 'b> Visitor<'a, std::fmt::Error> for BriefDisplayVisitor<'a, 'b> {
    fn visit_simple(&mut self, item: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
        match item.kind() {
            ItemKind::Str(s) if s.len() > self.text_length => {
                let s = s.as_cow();
                let end = s.char_indices().nth(self.text_length).unwrap_or_default().0;
                if end == s.len() {
                    write!(self.fmt, "\"{}\"", s.escape_debug())?;
                } else {
                    write!(self.fmt, "\"{}\"", s[..end].escape_debug())?;
                }
                Ok(())
            }
            _ => self.fmt.visit_simple(item),
        }
    }

    fn visit_array_begin(
        &mut self,
        array: TaggedItem<'a>,
        size: Option<u64>,
    ) -> Result<bool, std::fmt::Error> {
        self.depth += 1;

        if self.depth > self.max_depth {
            write!(self.fmt, "[…]")?;
            Ok(false)
        } else {
            self.fmt.visit_array_begin(array, size)
        }
    }

    fn visit_array_index(
        &mut self,
        _array: TaggedItem<'a>,
        index: u64,
    ) -> Result<bool, std::fmt::Error> {
        if index == self.array_length {
            write!(self.fmt, "")?;
        } else if index > 0 && index < self.array_length {
            write!(self.fmt, ", ")?;
        }
        Ok(index < self.array_length)
    }

    fn visit_array_end(&mut self, array: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
        if self.depth <= self.max_depth {
            self.fmt.visit_array_end(array)?;
        }
        self.depth -= 1;
        Ok(())
    }

    fn visit_dict_begin(
        &mut self,
        dict: TaggedItem<'a>,
        size: Option<u64>,
    ) -> Result<bool, std::fmt::Error> {
        self.depth += 1;

        if self.depth > self.max_depth {
            write!(self.fmt, "{{}}")?;
            Ok(false)
        } else {
            self.fmt.visit_dict_begin(dict, size)
        }
    }

    fn visit_dict_key(
        &mut self,
        dict: TaggedItem<'a>,
        key: TaggedItem<'a>,
        is_first: bool,
    ) -> Result<bool, std::fmt::Error> {
        if let Some(key) = key.decode().as_str() {
            if self.censored_properties.iter().any(|k| k == key.as_ref()) {
                write!(self.fmt, "")?;
                return Ok(false);
            }
        }
        self.fmt.visit_dict_key(dict, key, is_first)
    }

    fn visit_dict_end(&mut self, dict: TaggedItem<'a>) -> Result<(), std::fmt::Error> {
        if self.depth <= self.max_depth {
            self.fmt.visit_dict_end(dict)?;
        }
        self.depth -= 1;
        Ok(())
    }
}