use std::{cell::RefCell, collections::HashSet};
use bc_components::{Digest, DigestProvider};
use bc_ur::UREncodable;
use super::FormatContextOpt;
use crate::{EdgeType, Envelope, FormatContext, with_format_context};
#[derive(Clone, Copy, Default)]
pub enum DigestDisplayFormat {
#[default]
Short,
Full,
UR,
}
#[derive(Clone, Default)]
pub struct TreeFormatOpts<'a> {
hide_nodes: bool,
highlighting_target: HashSet<Digest>,
context: FormatContextOpt<'a>,
digest_display: DigestDisplayFormat,
}
impl<'a> TreeFormatOpts<'a> {
pub fn hide_nodes(mut self, hide: bool) -> Self {
self.hide_nodes = hide;
self
}
pub fn highlighting_target(mut self, target: HashSet<Digest>) -> Self {
self.highlighting_target = target;
self
}
pub fn context(mut self, context: FormatContextOpt<'a>) -> Self {
self.context = context;
self
}
pub fn digest_display(mut self, opt: DigestDisplayFormat) -> Self {
self.digest_display = opt;
self
}
}
impl Envelope {
pub fn tree_format(&self) -> String {
self.tree_format_opt(&TreeFormatOpts::default())
}
pub fn tree_format_opt(&self, opts: &TreeFormatOpts<'_>) -> String {
let elements: RefCell<Vec<TreeElement>> = RefCell::new(Vec::new());
let visitor = |envelope: &Envelope,
level: usize,
incoming_edge: EdgeType,
_: ()|
-> (_, bool) {
let elem = TreeElement::new(
level,
envelope.clone(),
incoming_edge,
!opts.hide_nodes,
opts.highlighting_target.contains(&envelope.digest()),
);
elements.borrow_mut().push(elem);
((), false)
};
self.walk(opts.hide_nodes, (), &visitor);
let elements = elements.borrow();
let format_elements =
|elements: &[TreeElement], context: &FormatContext| -> String {
elements
.iter()
.map(|e| e.string(context, opts.digest_display))
.collect::<Vec<_>>()
.join("\n")
};
match &opts.context {
FormatContextOpt::None => {
let context_ref = &FormatContext::default();
format_elements(&elements, context_ref)
}
FormatContextOpt::Global => {
with_format_context!(|context| {
format_elements(&elements, context)
})
}
FormatContextOpt::Custom(ctx) => format_elements(&elements, ctx),
}
}
}
impl Envelope {
pub fn short_id(&self, opt: DigestDisplayFormat) -> String {
match opt {
DigestDisplayFormat::Short => self.digest().short_description(),
DigestDisplayFormat::Full => self.digest().hex(),
DigestDisplayFormat::UR => self.digest().ur_string(),
}
}
}
#[derive(Debug)]
struct TreeElement {
level: usize,
envelope: Envelope,
incoming_edge: EdgeType,
show_id: bool,
is_highlighted: bool,
}
impl TreeElement {
fn new(
level: usize,
envelope: Envelope,
incoming_edge: EdgeType,
show_id: bool,
is_highlighted: bool,
) -> Self {
Self {
level,
envelope,
incoming_edge,
show_id,
is_highlighted,
}
}
fn string(
&self,
context: &FormatContext,
digest_display: DigestDisplayFormat,
) -> String {
let line = vec![
if self.is_highlighted {
Some("*".to_string())
} else {
None
},
if self.show_id {
Some(self.envelope.short_id(digest_display))
} else {
None
},
self.incoming_edge.label().map(|s| s.to_string()),
Some(self.envelope.summary(40, context)),
]
.into_iter()
.flatten()
.collect::<Vec<_>>()
.join(" ");
let indent = " ".repeat(self.level * 4);
format!("{}{}", indent, line)
}
}