Skip to content

Commit

Permalink
Merge pull request #29 from rust-scraper/display
Browse files Browse the repository at this point in the history
impl Display for Tree
  • Loading branch information
cfvescovo committed Aug 22, 2024
2 parents c6dd38e + 129ad48 commit f43f884
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
target
Cargo.lock
.vscode
89 changes: 89 additions & 0 deletions src/display.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::fmt::Display;

/// Indentation token
#[derive(Debug)]
struct Token {
/// Is followed by a brother
siblings: bool,
/// Is intermediate while printing children
children: bool,
}

impl Display for Token {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let Token { siblings, children } = self;

write!(
f,
"{}",
match (siblings, children) {
(true, true) => "│ ",
(true, false) => "├── ",
(false, true) => " ",
(false, false) => "└── ",
}
)
}
}

impl Token {
/// Create a new indentation token
fn new(siblings: bool) -> Self {
Token {
siblings,
children: false,
}
}

/// Set children flag before starting displaying children
fn set_children(&mut self) {
self.children = true;
}
}

/// Manages the state during the display operation
#[derive(Debug)]
pub struct Indentation {
tokens: Vec<Token>,
ignore_root: bool,
}

impl Display for Indentation {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let first: usize = if self.ignore_root { 1 } else { 0 };

for token in &self.tokens[first..] {
write!(f, "{token}")?;
}

Ok(())
}
}

impl Indentation {
/// Creates a new indentation handler
pub fn new(ignore_root: bool) -> Self {
Indentation {
tokens: Vec::new(),
ignore_root,
}
}

/// Adds a new layer of indentation
pub fn indent(&mut self, siblings: bool) -> &mut Self {
// Setup children mode for previous tokens
let len = self.tokens.len();
if len > 0 {
self.tokens[len - 1].set_children();
}

self.tokens.push(Token::new(siblings));
self
}

/// Removes the last layer of indentation
pub fn deindent(&mut self) -> &mut Self {
self.tokens.pop();
self
}
}
33 changes: 32 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
missing_copy_implementations
)]

use std::fmt::{self, Debug, Formatter};
use std::fmt::{self, Debug, Display, Formatter};
use std::num::NonZeroUsize;

/// Vec-backed ID-tree.
Expand Down Expand Up @@ -718,3 +718,34 @@ impl<T: Debug> Debug for Tree<T> {
}
}
}

// Handles display
mod display;

impl<T: Display> Display for Tree<T> {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
use display::Indentation;
use iter::Edge;

let mut indent: Indentation = Indentation::new(true);

for edge in self.root().traverse() {
match edge {
Edge::Open(node) if node.has_children() => {
indent.indent(node.next_sibling().is_some());
writeln!(f, "{indent}{}", node.value())?;
}
Edge::Open(node) => {
indent.indent(node.next_sibling().is_some());
writeln!(f, "{indent}{}", node.value())?;
indent.deindent();
}
Edge::Close(node) if node.has_children() => {
indent.deindent();
}
_ => {}
}
}
Ok(())
}
}
32 changes: 32 additions & 0 deletions tests/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,3 +156,35 @@ fn insert_id_before() {
);
assert_eq!(3, tree.root().children().count());
}

#[test]
fn test_display() {
let tree = tree! {
"root" => {
"a" => {
"child 1",
},
"b" => {
"child 2",
},
}
};

let repr = format!("{tree}");
let expected = "root\n├── a\n│ └── child 1\n└── b\n └── child 2\n";

assert_eq!(repr, expected);

let tree = tree! {
"root" => {
"a", "b" => {
"x", "y"
}, "c"
}
};

let repr = format!("{tree}");
let expected = "root\n├── a\n├── b\n│ ├── x\n│ └── y\n└── c\n";

assert_eq!(repr, expected);
}

0 comments on commit f43f884

Please sign in to comment.