bluegum 0.1.2

A tree printer with rich formatting, alternate values, debug info, and flexible structure - perfect for ASTs and complex data visualization
Documentation
// Copyright Two Neutron Stars Incorporated and contributors
// SPDX-License-Identifier: BlueOak-1.0.0

//! Traits and implementations for rendering collections of nodes.
//!
//! This module provides the [`Nodes`] and [`NodesWithState`] traits that allow
//! collections of [`Bluegum`] items to be rendered as groups of child nodes.

use {
  crate::{
    Bluegum,
    BluegumWithState,
    Builder,
  },
  std::ops::Deref,
};

/// A trait for collections of items that can be rendered as multiple tree
/// nodes.
///
/// The `Nodes` trait allows collections like `Vec<T>`, `&[T]`, and `Box<T>` to
/// be used with [`Builder::add_nodes`] to add multiple child nodes at once.
/// This is more convenient than manually iterating and calling
/// [`Builder::add_node`] for each item.
///
/// # Examples
///
/// ```rust
/// use bluegum::{Bluegum, Builder};
///
/// struct Parent {
///   children: Vec<String>,
/// }
///
/// impl Bluegum for Parent {
///   fn node(&self, b: &mut Builder) {
///     b.name("Parent")
///       .add_nodes("children", &self.children); // Uses Nodes trait
///   }
/// }
/// ```
///
/// # Automatic Implementations
///
/// This trait is automatically implemented for:
/// - `&Vec<T>` where `T: Bluegum`
/// - `&[T]` where `T: Bluegum`
/// - `&Box<T>` where `T: Bluegum`
pub trait Nodes<'a> {
  /// Render all items in this collection as a vector of builders.
  ///
  /// This method is called internally by [`Builder::add_nodes`] to convert
  /// the collection into individual tree nodes.
  fn render(self) -> Vec<Builder>;
}

/// Implementation of [`Nodes`] for boxed items.
///
/// This allows a single boxed item to be treated as a collection of one node.
impl<'a, T> Nodes<'a> for &'a Box<T>
where
  T: Bluegum,
{
  fn render(self) -> Vec<Builder> {
    vec![Builder::render(self.deref())]
  }
}

/// Implementation of [`Nodes`] for vectors.
///
/// This allows `Vec<T>` to be used directly with [`Builder::add_nodes`].
impl<'a, T> Nodes<'a> for &Vec<T>
where
  T: Bluegum,
{
  fn render(self) -> Vec<Builder> {
    self.iter().map(Builder::render).collect::<Vec<Builder>>()
  }
}

/// Implementation of [`Nodes`] for slices.
///
/// This allows `&[T]` to be used directly with [`Builder::add_nodes`].
impl<'a, T> Nodes<'a> for &[T]
where
  T: Bluegum,
{
  fn render(self) -> Vec<Builder> {
    self.iter().map(Builder::render).collect::<Vec<Builder>>()
  }
}

/// A stateful variant of [`Nodes`] for collections that need external context.
///
/// This trait is similar to [`Nodes`] but allows the rendering process to
/// access external state, such as symbol tables or type information. It's used
/// with [`Builder::add_nodes_with_state`].
///
/// # Examples
///
/// ```rust
/// use bluegum::{
///   Bluegum,
///   BluegumWithState,
///   Builder,
/// };
///
/// struct SymbolTable {
///   names: Vec<String>,
/// }
///
/// struct Variable {
///   name_id: usize,
/// }
///
/// impl Bluegum for Variable {
///   fn node(&self, b: &mut Builder) {
///     b.name("Variable").field("name_id", &self.name_id);
///   }
/// }
///
/// impl BluegumWithState<SymbolTable> for Variable {
///   fn node_with_state(&self, b: &mut Builder, symbols: &SymbolTable) {
///     b.name("Variable")
///       .field("name", &symbols.names[self.name_id]);
///   }
/// }
///
/// struct Block {
///   variables: Vec<Variable>,
/// }
///
/// impl Bluegum for Block {
///   fn node(&self, b: &mut Builder) {
///     b.name("Block").add_nodes("variables", &self.variables);
///   }
/// }
///
/// impl BluegumWithState<SymbolTable> for Block {
///   fn node_with_state(&self, b: &mut Builder, symbols: &SymbolTable) {
///     b.name("Block").add_nodes_with_state(
///       symbols,
///       "variables",
///       &self.variables,
///     );
///   }
/// }
/// ```
///
/// # Automatic Implementations
///
/// This trait is automatically implemented for:
/// - `&Vec<T>` where `T: BluegumWithState<State>`
/// - `&[T]` where `T: BluegumWithState<State>`
/// - `&Box<T>` where `T: BluegumWithState<State>`
pub trait NodesWithState<'a, State: ?Sized> {
  /// Render all items in this collection as a vector of builders using the
  /// provided state.
  ///
  /// This method is called internally by [`Builder::add_nodes_with_state`] to
  /// convert the collection into individual tree nodes with access to
  /// external state.
  fn render_with_state(self, state: &State) -> Vec<Builder>;
}

/// Implementation of [`NodesWithState`] for boxed items.
///
/// This allows a single boxed item to be treated as a stateful collection of
/// one node.
impl<'a, State: ?Sized, T> NodesWithState<'a, State> for &'a Box<T>
where
  T: BluegumWithState<State>,
{
  fn render_with_state(self, state: &State) -> Vec<Builder> {
    vec![Builder::render_with_state(self.deref(), state)]
  }
}

/// Implementation of [`NodesWithState`] for vectors.
///
/// This allows `Vec<T>` to be used directly with
/// [`Builder::add_nodes_with_state`].
impl<'a, State: ?Sized, T> NodesWithState<'a, State> for &Vec<T>
where
  T: BluegumWithState<State>,
{
  fn render_with_state(self, state: &State) -> Vec<Builder> {
    self
      .iter()
      .map(|node| Builder::render_with_state(node, state))
      .collect::<Vec<Builder>>()
  }
}

/// Implementation of [`NodesWithState`] for slices.
///
/// This allows `&[T]` to be used directly with
/// [`Builder::add_nodes_with_state`].
impl<'a, State: ?Sized, T> NodesWithState<'a, State> for &[T]
where
  T: BluegumWithState<State>,
{
  fn render_with_state(self, state: &State) -> Vec<Builder> {
    self
      .iter()
      .map(|node| Builder::render_with_state(node, state))
      .collect::<Vec<Builder>>()
  }
}