From b45c77c0fcc8c6efb22a76d375882e2b2a517b61 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 22 Aug 2024 12:47:58 +0200 Subject: [PATCH 1/3] implement Display for Tree --- .gitignore | 1 + src/lib.rs | 40 +++++++++++++++++++++++++++++++++++++++- tests/tree.rs | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a9d37c5..110d010 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +.vscode \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 0a0257b..99b3137 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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. @@ -718,3 +718,41 @@ impl Debug for Tree { } } } + +impl Display for Tree { + fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + use iter::Edge; + + fn indent(f: &mut Formatter, level: usize) -> Result<(), fmt::Error> { + for _ in 0..level { + write!(f, " ")?; + } + Ok(()) + } + + let mut level: usize = 0; + + for edge in self.root().traverse() { + match edge { + Edge::Open(node) if node.has_children() => { + indent(f, level)?; + write!(f, "{}\n", node.value())?; + level += 1; + } + Edge::Open(node) if node.next_sibling().is_some() => { + indent(f, level)?; + write!(f, "{}\n", node.value())?; + } + Edge::Open(node) => { + indent(f, level)?; + write!(f, "{}\n", node.value())?; + } + Edge::Close(node) if node.has_children() => { + level -= 1; + } + _ => {} + } + } + Ok(()) + } +} diff --git a/tests/tree.rs b/tests/tree.rs index ac22561..dc2683c 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -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); +} From 45de3ddc35f62a52f6739f6e73cdd27435d4a168 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 22 Aug 2024 16:37:40 +0200 Subject: [PATCH 2/3] prettier print with indentation object --- src/display.rs | 86 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 30 ++++++++---------- tests/tree.rs | 4 +-- 3 files changed, 102 insertions(+), 18 deletions(-) create mode 100644 src/display.rs diff --git a/src/display.rs b/src/display.rs new file mode 100644 index 0000000..f552aff --- /dev/null +++ b/src/display.rs @@ -0,0 +1,86 @@ +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 ToString for Token { + fn to_string(&self) -> String { + let Token { siblings, children } = self; + + match (siblings, children) { + (true, true) => "│ ", + (true, false) => "├── ", + (false, true) => " ", + (false, false) => "└── ", + } + .to_string() + } +} + +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, + 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.to_string())?; + } + + 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 + } +} diff --git a/src/lib.rs b/src/lib.rs index 99b3137..b7ce9d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -719,36 +719,34 @@ impl Debug for Tree { } } +// Handles display +mod display; + impl Display for Tree { fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> { + use display::Indentation; use iter::Edge; - fn indent(f: &mut Formatter, level: usize) -> Result<(), fmt::Error> { - for _ in 0..level { - write!(f, " ")?; - } - Ok(()) - } - - let mut level: usize = 0; + let mut indent: Indentation = Indentation::new(true); for edge in self.root().traverse() { match edge { Edge::Open(node) if node.has_children() => { - indent(f, level)?; - write!(f, "{}\n", node.value())?; - level += 1; + indent.indent(node.next_sibling().is_some()); + write!(f, "{}{}\n", indent.to_string(), node.value())?; } Edge::Open(node) if node.next_sibling().is_some() => { - indent(f, level)?; - write!(f, "{}\n", node.value())?; + indent.indent(node.next_sibling().is_some()); + write!(f, "{}{}\n", indent.to_string(), node.value())?; + indent.deindent(); } Edge::Open(node) => { - indent(f, level)?; - write!(f, "{}\n", node.value())?; + indent.indent(node.next_sibling().is_some()); + write!(f, "{}{}\n", indent.to_string(), node.value())?; + indent.deindent(); } Edge::Close(node) if node.has_children() => { - level -= 1; + indent.deindent(); } _ => {} } diff --git a/tests/tree.rs b/tests/tree.rs index dc2683c..78eee6a 100644 --- a/tests/tree.rs +++ b/tests/tree.rs @@ -171,7 +171,7 @@ fn test_display() { }; let repr = format!("{tree}"); - let expected = "root\n a\n child 1\n b\n child 2\n"; + let expected = "root\n├── a\n│ └── child 1\n└── b\n └── child 2\n"; assert_eq!(repr, expected); @@ -184,7 +184,7 @@ fn test_display() { }; let repr = format!("{tree}"); - let expected = "root\n a\n b\n x\n y\n c\n"; + let expected = "root\n├── a\n├── b\n│ ├── x\n│ └── y\n└── c\n"; assert_eq!(repr, expected); } From 129ad483cd6aa04d696b23496220659580486d42 Mon Sep 17 00:00:00 2001 From: LoZack19 Date: Thu, 22 Aug 2024 16:56:00 +0200 Subject: [PATCH 3/3] refactor with clippy --- src/display.rs | 23 +++++++++++++---------- src/lib.rs | 9 ++------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/display.rs b/src/display.rs index f552aff..5744f39 100644 --- a/src/display.rs +++ b/src/display.rs @@ -9,17 +9,20 @@ struct Token { children: bool, } -impl ToString for Token { - fn to_string(&self) -> String { +impl Display for Token { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { let Token { siblings, children } = self; - match (siblings, children) { - (true, true) => "│ ", - (true, false) => "├── ", - (false, true) => " ", - (false, false) => "└── ", - } - .to_string() + write!( + f, + "{}", + match (siblings, children) { + (true, true) => "│ ", + (true, false) => "├── ", + (false, true) => " ", + (false, false) => "└── ", + } + ) } } @@ -50,7 +53,7 @@ impl Display for Indentation { let first: usize = if self.ignore_root { 1 } else { 0 }; for token in &self.tokens[first..] { - write!(f, "{}", token.to_string())?; + write!(f, "{token}")?; } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index b7ce9d2..d84d563 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -733,16 +733,11 @@ impl Display for Tree { match edge { Edge::Open(node) if node.has_children() => { indent.indent(node.next_sibling().is_some()); - write!(f, "{}{}\n", indent.to_string(), node.value())?; - } - Edge::Open(node) if node.next_sibling().is_some() => { - indent.indent(node.next_sibling().is_some()); - write!(f, "{}{}\n", indent.to_string(), node.value())?; - indent.deindent(); + writeln!(f, "{indent}{}", node.value())?; } Edge::Open(node) => { indent.indent(node.next_sibling().is_some()); - write!(f, "{}{}\n", indent.to_string(), node.value())?; + writeln!(f, "{indent}{}", node.value())?; indent.deindent(); } Edge::Close(node) if node.has_children() => {