use {
crate::{
nodes::NodesWithState,
prefix::Prefix,
printer::PrinterInner,
writer::Writer,
Bluegum,
BluegumWithState,
Nodes,
},
std::borrow::BorrowMut,
};
pub(crate) type FieldDebug = Vec<(String, String)>;
const MAX_RENDER_DEPTH: usize = 256;
std::thread_local! {
static RENDER_DEPTH: std::cell::Cell<usize> =
const { std::cell::Cell::new(0) };
}
fn with_render_depth(builder: &mut Builder, f: impl FnOnce(&mut Builder)) {
let depth = RENDER_DEPTH.with(|d| d.get());
if depth >= MAX_RENDER_DEPTH {
builder.name("… depth limit reached");
return;
}
RENDER_DEPTH.with(|d| d.set(depth + 1));
f(builder);
RENDER_DEPTH.with(|d| d.set(depth));
}
#[derive(Debug, Clone)]
pub(crate) struct BuilderName {
pub(crate) name: String,
pub(crate) alt: Option<String>,
pub(crate) debug: FieldDebug,
}
#[derive(Debug, Clone)]
pub(crate) enum Field {
KeyValue {
name: String,
value: String,
alt: Option<String>,
debug: FieldDebug,
},
Divider,
}
impl Field {
pub(crate) fn set_alt(&mut self, alt: String) {
match self {
| Field::KeyValue { alt: ref mut a, .. } => *a = Some(alt),
| Field::Divider => (),
}
}
pub(crate) fn set_debug(&mut self, debug: (String, String)) {
match self {
| Field::KeyValue {
debug: ref mut d, ..
} => d.push(debug),
| Field::Divider => (),
}
}
}
pub(crate) type Fields = Vec<Field>;
#[derive(Debug, Clone)]
pub struct Builder {
pub(crate) name: BuilderName,
pub(crate) fields: Fields,
pub(crate) unlabled_nodes: Vec<Builder>,
pub(crate) nodes: Vec<(String, Vec<Builder>)>,
pub(crate) total_nodes_len: usize,
}
impl Builder {
pub fn new() -> Self {
Self {
name: BuilderName {
name: "".to_string(),
alt: None,
debug: vec![],
},
fields: Vec::new(),
nodes: Vec::new(),
unlabled_nodes: Vec::new(),
total_nodes_len: 0,
}
}
}
impl Default for Builder {
fn default() -> Self {
Self::new()
}
}
impl Builder {
pub fn render<T: Bluegum>(node: &T) -> Self {
let mut builder = Builder::new();
with_render_depth(&mut builder, |b| Bluegum::node(node, b));
builder
}
pub fn render_with_state<State: ?Sized, T: BluegumWithState<State>>(
node: &T,
state: &State,
) -> Self {
let mut builder = Builder::new();
with_render_depth(&mut builder, |b| {
<dyn BluegumWithState<State>>::node_with_state(node, b, state);
});
builder
}
}
impl Builder {
pub fn name(&mut self, name: &str) -> &mut Self {
self.name.name = name.to_string();
self
}
pub fn get_name(&self) -> &str {
&self.name.name
}
pub fn divider(&mut self) -> &mut Self {
self.fields.push(Field::Divider);
self
}
pub fn field(
&mut self,
name: &str,
value: impl std::fmt::Display,
) -> &mut Self {
self.fields.push(Field::KeyValue {
name: name.to_string(),
value: format!("{value}"),
alt: None,
debug: vec![],
});
self
}
pub fn option_field(
&mut self,
name: &str,
value: Option<impl std::fmt::Display>,
) -> &mut Self {
value.map(|val| self.field(name, val));
self
}
pub fn alt(&mut self, value: impl std::fmt::Display) -> &mut Self {
if self.fields.is_empty() {
self.name.alt = Some(format!("{value}"));
} else if let Some(mut last) = self.fields.last_mut() {
last.set_alt(format!("{value}"));
}
self
}
pub fn option_alt(
&mut self,
value: Option<impl std::fmt::Display>,
) -> &mut Self {
value.map(|val| self.alt(val));
self
}
pub fn debug(
&mut self,
name: &str,
value: impl std::fmt::Display,
) -> &mut Self {
if self.fields.is_empty() {
self.name.debug.push((name.to_string(), format!("{value}")));
} else if let Some(mut last) = self.fields.last_mut() {
last.set_debug((name.to_string(), format!("{value}")));
}
self
}
pub fn option_debug(
&mut self,
name: &str,
value: Option<impl std::fmt::Display>,
) -> &mut Self {
value.map(|val| self.debug(name, val));
self
}
pub fn add_node(&mut self, name: &str, node: &dyn Bluegum) -> &mut Self {
let mut builder = Builder::new();
with_render_depth(&mut builder, |b| Bluegum::node(node, b));
self.total_nodes_len += 1;
self.nodes.push((name.to_string(), vec![builder]));
self
}
pub fn add_option_node(
&mut self,
name: &str,
node: Option<&dyn Bluegum>,
) -> &mut Self {
node.map(|n| self.add_node(name, n));
self
}
pub fn add_node_with_state<State: ?Sized, T: BluegumWithState<State>>(
&mut self,
state: &State,
name: &str,
c: &T,
) -> &mut Self {
let mut builder = Builder::new();
with_render_depth(&mut builder, |b| c.node_with_state(b, state));
self.total_nodes_len += 1;
self.nodes.push((name.to_string(), vec![builder]));
self
}
pub fn add_unlabled_node(&mut self, node: &dyn Bluegum) -> &mut Self {
let mut builder = Builder::new();
with_render_depth(&mut builder, |b| node.node(b));
self.total_nodes_len += 1;
self.unlabled_nodes.push(builder);
self
}
pub fn add_unlabled_node_with_state<State: ?Sized, T: BluegumWithState<State>>(
&mut self,
state: &State,
c: &T,
) -> &mut Self {
let mut builder = Builder::new();
with_render_depth(&mut builder, |b| c.node_with_state(b, state));
self.total_nodes_len += 1;
self.unlabled_nodes.push(builder);
self
}
pub fn add_option_node_with_state<State: ?Sized, T: BluegumWithState<State>>(
&mut self,
state: &State,
name: &str,
node: Option<&T>,
) -> &mut Self {
node.map(|n| self.add_node_with_state(state, name, n));
self
}
pub fn add_nodes<'a>(
&mut self,
name: &str,
nodes: impl Nodes<'a>,
) -> &mut Self {
let c = nodes.render();
self.total_nodes_len += c.len();
self.nodes.push((name.to_string(), c));
self
}
pub fn add_option_nodes<'a>(
&mut self,
name: &str,
nodes: Option<impl Nodes<'a>>,
) -> &mut Self {
nodes.map(|nds| self.add_nodes(name, nds));
self
}
pub fn add_nodes_with_state<'a, State: ?Sized>(
&mut self,
state: &State,
name: &str,
nodes: impl NodesWithState<'a, State>,
) -> &mut Self {
let c = nodes.render_with_state(state);
self.total_nodes_len += c.len();
self.nodes.push((name.to_string(), c));
self
}
pub fn add_nodes_of_builders(
&mut self,
name: &str,
nodes: Vec<Builder>,
) -> &mut Self {
self.total_nodes_len += nodes.len();
self.nodes.push((name.to_string(), nodes));
self
}
pub fn add_builders(&mut self, builders: Vec<Builder>) -> &mut Self {
self.total_nodes_len += builders.len();
self.unlabled_nodes.extend(builders);
self
}
pub fn add_builder(&mut self, builder: Builder) -> &mut Self {
self.total_nodes_len += 1;
self.unlabled_nodes.push(builder);
self
}
pub fn add_named_builder(&mut self, name: &str, node: Builder) -> &mut Self {
self.total_nodes_len += 1;
self.nodes.push((name.to_string(), vec![node]));
self
}
}