gooey-rs 0.12.1

Tile-based UI library with audio support
Documentation
//! Utility functions

use std::collections::VecDeque;
use crate::interface;
use crate::Tree;
use crate::tree::{self, NodeId};

/// Utility functions to extend the Tree interface for common operations.
pub trait TreeHelper {
  fn get_element (&self, node_id : &NodeId) -> &interface::Element;
  fn get_element_mut (&mut self, node_id : &NodeId) -> &mut interface::Element;
  fn get_parent (&self, child : &NodeId) -> &interface::Element;
  fn get_parent_mut (&mut self, child : &NodeId) -> &mut interface::Element;
  fn get_parent_id (&self, child : &NodeId) -> &NodeId;
  /// Splice the subtree of `other` rooted at `other_id` into `tree` under
  /// `parent_id` with the given `order`
  fn splice_subtree (
    &mut self,
    parent_id : &NodeId,
    other     : &Tree <interface::Element>,
    other_id  : &NodeId,
    order     : interface::CreateOrder
  ) -> NodeId;
  /// Write formatted tree representation showing only the names of the nodes
  /// (as opposed to the default `Tree::write_formatted` method).
  fn write_formatted_names <W : std::fmt::Write> (&self, w : &mut W)
    -> std::fmt::Result;
}

/// Map a function over the nodes of a tree returning a new tree
pub fn map_tree <T, U, F> (tree : &Tree <T>, f : F) -> Tree <U> where
  F : Fn (&T) -> U
{
  use tree::{InsertBehavior, Node};
  let mut out = Tree::new();
  if let Some (root_id) = tree.root_node_id() {
    let root = tree.get (root_id).unwrap();
    let new_root_id = out
      .insert (Node::new (f (root.data())), InsertBehavior::AsRoot).unwrap();
    let mut parent_child_ids = root.children().iter()
      .map (|child_id| (new_root_id.clone(), child_id.clone()))
      .collect::<VecDeque <_>>();
    while let Some ((new_parent_id, old_child_id)) =
      parent_child_ids.pop_front()
    {
      let child = tree.get (&old_child_id).unwrap();
      let new_child_id = out.insert (
        Node::new (f (child.data())),
        InsertBehavior::UnderNode (&new_parent_id)
      ).unwrap();
      for child_id in child.children().iter() {
        parent_child_ids.push_back ((new_child_id.clone(), child_id.clone()));
      }
    }
  }
  out
}

impl TreeHelper for Tree <interface::Element> {
  /// Unwraps and gets the underlying `Element` reference.
  ///
  /// &#9888; Panics if `node_id` does not exist.
  #[inline]
  fn get_element (&self, node_id : &NodeId) -> &interface::Element {
    self.get (node_id).unwrap().data()
  }

  /// Unwraps and gets the underlying `Element` reference.
  ///
  /// &#9888; Panics if `node_id` does not exist.
  #[inline]
  fn get_element_mut (&mut self, node_id : &NodeId) -> &mut interface::Element {
    self.get_mut (node_id).unwrap().data_mut()
  }

  /// Unwraps and gets the parent of the referenced `NodeId`.
  ///
  /// &#9888; Panics if `child` does not exist or if `child` does not have a parent.
  #[inline]
  fn get_parent (&self, child : &NodeId) -> &interface::Element {
    self.get_element (self.get_parent_id (child))
  }

  /// Unwraps and gets the parent of the referenced `NodeId`.
  ///
  /// &#9888; Panics if `child` does not exist or if `child` does not have a parent.
  #[inline]
  fn get_parent_mut (&mut self, child : &NodeId) -> &mut interface::Element {
    let parent_id = self.get_parent_id (child).clone();
    self.get_element_mut (&parent_id)
  }

  /// Unwraps and gets the parent ID of the referenced `NodeId`.
  ///
  /// &#9888; Panics if `child` does not exist or if `child` does not have a parent.
  #[inline]
  fn get_parent_id (&self, child : &NodeId) -> &NodeId {
    self.get (child).unwrap().parent().unwrap()
  }

  /// Splice the subtree of `other` rooted at `other_id` into `tree` under `parent_id`
  /// with the given `order`
  fn splice_subtree (
    &mut self,
    parent_id : &NodeId,
    other     : &Tree <interface::Element>,
    other_id  : &NodeId,
    order     : interface::CreateOrder
  ) -> NodeId {
    use tree::Node;
    use interface::CreateOrder;
    let subtree_root = Node::new (other.get_element (other_id).clone());
    let subtree_id   = self.insert (
      subtree_root, tree::InsertBehavior::UnderNode (parent_id)
    ).unwrap();
    match order {
      CreateOrder::Append  => {}
      CreateOrder::Prepend => {
        let _ = self.make_first_sibling (&subtree_id).unwrap();
      }
      CreateOrder::NthSibling (n) =>
        self.make_nth_sibling (&subtree_id, n as usize).unwrap()
    }
    for child_id in other.children_ids (other_id).unwrap() {
      self.splice_subtree (&subtree_id, other, child_id, CreateOrder::Append);
    }
    subtree_id
  }

  fn write_formatted_names<W: std::fmt::Write>(&self, w: &mut W)
    -> std::fmt::Result
  {
    if let Some(node_id) = self.root_node_id() {
      let childn = 0;
      let level = 0;
      let last = vec![];
      let mut stack = vec![(node_id, childn, level, last)];
      while let Some((node_id, childn, level, last)) = stack.pop() {
        debug_assert_eq!(
          last.len(), level,
          "each previous level should indicate whether it has reached the last node"
        );
        let node = self.get(node_id)
          .expect("getting node of existing node ref id");
        if childn == 0 {
          for i in 1..level {
            if last[i - 1] {
              write!(w, "    ")?;
            } else {
              write!(w, "")?;
            }
          }
          if level > 0 {
            if last[level - 1] {
              write!(w, "└── ")?;
            } else {
              write!(w, "├── ")?;
            }
          }
          let data  = node.data();
          let state = match data.controller.state {
            interface::controller::State::Focused  => "*",
            interface::controller::State::Enabled  => "",
            interface::controller::State::Disabled => ""
          };
          writeln!(w, "{}{}", data.name, state)?;
        }
        let mut children = node.children().iter().skip(childn);
        if let Some(child_id) = children.next() {
          let mut next_last = last.clone();
          if children.next().is_some() {
            stack.push((node_id, childn + 1, level, last));
            next_last.push(false);
          } else {
            next_last.push(true);
          }
          stack.push((child_id, 0, level + 1, next_last));
        }
      }
    }
    Ok(())
  }
}

#[cfg(test)]
mod tests {
  use crate::tree::{InsertBehavior, Node};
  use super::*;
  #[test]
  fn map_tree() {
    use super::map_tree;
    let mut tree = Tree::new();
    let root_id = tree.insert (Node::new (0u32), InsertBehavior::AsRoot).unwrap();
    let child_id = tree.insert (Node::new (1), InsertBehavior::UnderNode (&root_id))
      .unwrap();
    tree.insert (Node::new (2), InsertBehavior::UnderNode (&root_id)).unwrap();
    tree.insert (Node::new (3), InsertBehavior::UnderNode (&child_id)).unwrap();
    tree.insert (Node::new (4), InsertBehavior::UnderNode (&child_id)).unwrap();
    let mut s = String::new();
    tree.write_formatted (&mut s).unwrap();
    println!("old tree:\n{s}");
    let new_tree : Tree <String> = map_tree (&tree, |i| (*i).to_string());
    let mut s = String::new();
    new_tree.write_formatted (&mut s).unwrap();
    println!("new tree:\n{s}");
  }
}