From 12641c93da0214434a4fa75893924b8b3b303b87 Mon Sep 17 00:00:00 2001 From: Kile Asmussen Date: Fri, 27 Mar 2026 23:45:55 +0100 Subject: [PATCH 1/8] . --- .vscode/cspell.json | 5 +- src/report/iter.rs | 123 ++++++++++++++++++++++++++++++++++++++++---- src/report/ref_.rs | 4 +- 3 files changed, 119 insertions(+), 13 deletions(-) diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 6f153a5..53e7f25 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -3,9 +3,12 @@ "minWordLength": 1, "words": [ "acq", + "BFS", "bz2", "cfgs", "ctx", + "Deque", + "DFS", "docsrs", "dyn", "env", @@ -52,4 +55,4 @@ "fromat: format", "hte: the" ] -} \ No newline at end of file +} diff --git a/src/report/iter.rs b/src/report/iter.rs index a0be46d..f118765 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -1,7 +1,10 @@ -use alloc::vec::Vec; +use alloc::{collections::vec_deque::VecDeque, string::ToString}; use core::{iter::FusedIterator, marker::PhantomData}; -use crate::{ReportRef, markers::Dynamic}; +use crate::{ + ReportRef, + markers::{Dynamic, Uncloneable}, +}; /// An iterator over a report and all its descendant reports in depth-first /// order. @@ -11,28 +14,104 @@ use crate::{ReportRef, markers::Dynamic}; /// manner, starting from the root report and visiting each child report before /// moving to the next sibling. #[must_use] -pub struct ReportIter<'a, Ownership: 'static, ThreadSafety: 'static> { - stack: Vec>, +pub struct ReportIter<'a, Ownership: 'static, ThreadSafety: 'static, Strategy: ?Sized = DFS> { + stack: VecDeque>, _ownership: PhantomData, _thread_safety: PhantomData, + _traversal: PhantomData<*mut Strategy>, } -impl<'a, O, T> ReportIter<'a, O, T> { +impl<'a, O, T, S: ?Sized> ReportIter<'a, O, T, S> { /// Creates a new [`ReportIter`] from a vector of raw report references - pub(crate) fn from_raw(stack: Vec>) -> Self { + pub(crate) fn from_raw(stack: VecDeque>) -> Self { Self { stack, _ownership: PhantomData, _thread_safety: PhantomData, + _traversal: PhantomData, } } } -impl<'a, O, T> Iterator for ReportIter<'a, O, T> { +#[cfg(test)] +fn report_tree() -> crate::Report { + use crate::report_collection::ReportCollection; + use alloc::format; + + (1..=2) + .map(|i| { + (1..=2) + .map(|j| report!(format!("{}.{}", i, j)).into_cloneable()) + .collect::() + .context(format!("{}", i)) + }) + .collect::() + .context(format!("root")) + .into_dynamic() +} + +#[cfg(test)] +fn join_contexts<'b, OW: 'static>( + it: impl Iterator>, +) -> alloc::string::String { + use alloc::vec::Vec; + + it.into_iter() + .map(|e: ReportRef<'_, Dynamic, OW>| e.format_current_context().to_string()) + .collect::>() + .join(" ") +} + +impl<'a, O, T> ReportIter<'a, O, T, DFS> { + /// Convert this traversal to a breadth-first search. + /// + /// **Warning:** if this function is called mid-traversal, + /// the result is unspecified behavior. Nothing unsound will + /// happen, but the traversal order will not be guaranteed. + /// + /// # Examples + /// + /// ```rust + /// # use rootcause::ReportIter; + /// let rep = ReportIter::report_tree(); + /// // root + /// // / \ + /// // 1 2 + /// // / \ / \ + /// // 1.1 1.2 2.1 2.2 + /// assert_eq!(ReportIter::join_contexts(rep.iter_reports()), "root 1 1.1 1.2 2 2.1 2.2"); + /// ``` + pub fn bfs(self) -> ReportIter<'a, O, T, BFS> { + ReportIter::from_raw(self.stack) + } +} + +impl<'a, O, T> ReportIter<'a, O, T, BFS> { + /// Convert this traversal to a depth-first search. + /// + /// **Warning:** if this function is called mid-traversal, + /// the result is unspecified behavior. Nothing unsound will + /// happen, but the traversal order will not be guaranteed. + pub fn dfs(self) -> ReportIter<'a, O, T, DFS> { + ReportIter::from_raw(self.stack) + } +} + +/// Marker type for depth-first traversal in the [`ReportIter`] type. +pub struct DFS { + _not_constructible: [()], +} + +/// Marker type for breadth-first traversal in the [`ReportIter`] type. +pub struct BFS { + _not_constructible: [()], +} + +impl<'a, O, T> Iterator for ReportIter<'a, O, T, DFS> { type Item = ReportRef<'a, Dynamic, O, T>; fn next(&mut self) -> Option { - let cur: ReportRef<'a, Dynamic, O, T> = self.stack.pop()?; + let cur: ReportRef<'a, Dynamic, O, T> = self.stack.pop_back()?; let new_children = cur .children() @@ -55,6 +134,30 @@ impl<'a, O, T> Iterator for ReportIter<'a, O, T> { } } -impl<'a, O, T> FusedIterator for ReportIter<'a, O, T> {} +impl<'a, O, T> Iterator for ReportIter<'a, O, T, BFS> { + type Item = ReportRef<'a, Dynamic, O, T>; + + fn next(&mut self) -> Option { + let cur: ReportRef<'a, Dynamic, O, T> = self.stack.pop_front()?; + + let new_children = cur.children().iter().map(|child_report| { + // SAFETY: + // 1. At this point we have an instance of a `ReportRef<'a, Dynamic, O, T>` in + // scope. This means we can invoke the safety invariants of that ReportRef. + // One of the safety invariants of that `ReportRef` is that `O` must either + // be `Cloneable` or `Uncloneable`. But this fulfills our requirements for + // calling `ReportRef::from_cloneable` using that same `O`. + unsafe { + // @add-unsafe-context: Dynamic + ReportRef::::from_cloneable(child_report) + } + }); + self.stack.extend(new_children); + Some(cur) + } +} + +impl<'a, O, T> FusedIterator for ReportIter<'a, O, T, DFS> {} +impl<'a, O, T> FusedIterator for ReportIter<'a, O, T, BFS> {} -impl<'a, O, T> Unpin for ReportIter<'a, O, T> {} +impl<'a, O, T, S> Unpin for ReportIter<'a, O, T, S> {} diff --git a/src/report/ref_.rs b/src/report/ref_.rs index 9bface1..e6165f7 100644 --- a/src/report/ref_.rs +++ b/src/report/ref_.rs @@ -1,4 +1,4 @@ -use alloc::vec; +use alloc::collections::vec_deque::VecDeque; use core::any::TypeId; use rootcause_internals::handlers::{ContextFormattingStyle, FormattingFunction}; @@ -503,7 +503,7 @@ impl<'a, C: ?Sized, O, T> ReportRef<'a, C, O, T> { /// assert_eq!(all_reports.len(), 6); /// ``` pub fn iter_reports(self) -> ReportIter<'a, O, T> { - let stack = vec![self.into_dynamic()]; + let stack = VecDeque::from([self.into_dynamic()]); ReportIter::from_raw(stack) } From 6068668c848a9c1558a94950c515b598196f25c1 Mon Sep 17 00:00:00 2001 From: Kile Asmussen Date: Fri, 27 Mar 2026 23:48:02 +0100 Subject: [PATCH 2/8] . --- src/report/iter.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/report/iter.rs b/src/report/iter.rs index f118765..1cc2d2c 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -1,10 +1,7 @@ -use alloc::{collections::vec_deque::VecDeque, string::ToString}; +use alloc::collections::vec_deque::VecDeque; use core::{iter::FusedIterator, marker::PhantomData}; -use crate::{ - ReportRef, - markers::{Dynamic, Uncloneable}, -}; +use crate::{ReportRef, markers::Dynamic}; /// An iterator over a report and all its descendant reports in depth-first /// order. @@ -54,8 +51,8 @@ fn report_tree() -> crate::Report { fn join_contexts<'b, OW: 'static>( it: impl Iterator>, ) -> alloc::string::String { + use alloc::string::ToString; use alloc::vec::Vec; - it.into_iter() .map(|e: ReportRef<'_, Dynamic, OW>| e.format_current_context().to_string()) .collect::>() @@ -73,13 +70,14 @@ impl<'a, O, T> ReportIter<'a, O, T, DFS> { /// /// ```rust /// # use rootcause::ReportIter; - /// let rep = ReportIter::report_tree(); + /// let rep = + /// # panic!("HOW THE FUCK DO I CALL MY HELPER FUNCTIONS YOU FUCKING MODULE SYSTEM"); /// // root /// // / \ /// // 1 2 /// // / \ / \ /// // 1.1 1.2 2.1 2.2 - /// assert_eq!(ReportIter::join_contexts(rep.iter_reports()), "root 1 1.1 1.2 2 2.1 2.2"); + /// assert_eq!("", "root 1 1.1 1.2 2 2.1 2.2"); /// ``` pub fn bfs(self) -> ReportIter<'a, O, T, BFS> { ReportIter::from_raw(self.stack) @@ -92,6 +90,20 @@ impl<'a, O, T> ReportIter<'a, O, T, BFS> { /// **Warning:** if this function is called mid-traversal, /// the result is unspecified behavior. Nothing unsound will /// happen, but the traversal order will not be guaranteed. + /// + /// # Examples + /// + /// ```rust + /// # use rootcause::ReportIter; + /// let rep = + /// # panic!("HOW THE FUCK DO I CALL MY HELPER FUNCTIONS YOU FUCKING MODULE SYSTEM"); + /// // root + /// // / \ + /// // 1 2 + /// // / \ / \ + /// // 1.1 1.2 2.1 2.2 + /// assert_eq!("", "root 1 1.1 1.2 2 2.1 2.2"); + /// ``` pub fn dfs(self) -> ReportIter<'a, O, T, DFS> { ReportIter::from_raw(self.stack) } From 19c80b1141afd2a09b8ae3eca22c5da6c5e14c57 Mon Sep 17 00:00:00 2001 From: Kile Asmussen Date: Sat, 28 Mar 2026 20:08:15 +0100 Subject: [PATCH 3/8] . --- src/report/iter.rs | 62 +++++++++++++++++++--------------------------- 1 file changed, 26 insertions(+), 36 deletions(-) diff --git a/src/report/iter.rs b/src/report/iter.rs index 1cc2d2c..5793811 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -1,7 +1,7 @@ -use alloc::collections::vec_deque::VecDeque; +use alloc::{collections::vec_deque::VecDeque, vec::Vec}; use core::{iter::FusedIterator, marker::PhantomData}; -use crate::{ReportRef, markers::Dynamic}; +use crate::{ReportRef, markers::Dynamic, report_collection::ReportCollection}; /// An iterator over a report and all its descendant reports in depth-first /// order. @@ -111,12 +111,32 @@ impl<'a, O, T> ReportIter<'a, O, T, BFS> { /// Marker type for depth-first traversal in the [`ReportIter`] type. pub struct DFS { - _not_constructible: [()], + _not_constructible: NotConstructible, } /// Marker type for breadth-first traversal in the [`ReportIter`] type. pub struct BFS { - _not_constructible: [()], + _not_constructible: NotConstructible, +} + +#[allow(missing_copy_implementations, reason = "not constructible")] +struct NotConstructible; + +fn list_children<'a, O: 'static, T>( + children: &'a ReportCollection, +) -> impl DoubleEndedIterator> { + children.iter().map(|child_report| { + // SAFETY: + // 1. At this point we have an instance of a `ReportRef<'a, Dynamic, O, T>` in + // scope. This means we can invoke the safety invariants of that ReportRef. + // One of the safety invariants of that `ReportRef` is that `O` must either + // be `Cloneable` or `Uncloneable`. But this fulfills our requirements for + // calling `ReportRef::from_cloneable` using that same `O`. + unsafe { + // @add-unsafe-context: Dynamic + ReportRef::::from_cloneable(child_report) + } + }) } impl<'a, O, T> Iterator for ReportIter<'a, O, T, DFS> { @@ -124,24 +144,7 @@ impl<'a, O, T> Iterator for ReportIter<'a, O, T, DFS> { fn next(&mut self) -> Option { let cur: ReportRef<'a, Dynamic, O, T> = self.stack.pop_back()?; - - let new_children = cur - .children() - .iter() - .map(|child_report| { - // SAFETY: - // 1. At this point we have an instance of a `ReportRef<'a, Dynamic, O, T>` in - // scope. This means we can invoke the safety invariants of that ReportRef. - // One of the safety invariants of that `ReportRef` is that `O` must either - // be `Cloneable` or `Uncloneable`. But this fulfills our requirements for - // calling `ReportRef::from_cloneable` using that same `O`. - unsafe { - // @add-unsafe-context: Dynamic - ReportRef::::from_cloneable(child_report) - } - }) - .rev(); - self.stack.extend(new_children); + self.stack.extend(list_children(cur.children()).rev()); Some(cur) } } @@ -151,20 +154,7 @@ impl<'a, O, T> Iterator for ReportIter<'a, O, T, BFS> { fn next(&mut self) -> Option { let cur: ReportRef<'a, Dynamic, O, T> = self.stack.pop_front()?; - - let new_children = cur.children().iter().map(|child_report| { - // SAFETY: - // 1. At this point we have an instance of a `ReportRef<'a, Dynamic, O, T>` in - // scope. This means we can invoke the safety invariants of that ReportRef. - // One of the safety invariants of that `ReportRef` is that `O` must either - // be `Cloneable` or `Uncloneable`. But this fulfills our requirements for - // calling `ReportRef::from_cloneable` using that same `O`. - unsafe { - // @add-unsafe-context: Dynamic - ReportRef::::from_cloneable(child_report) - } - }); - self.stack.extend(new_children); + self.stack.extend(list_children(cur.children())); Some(cur) } } From dcb4225f4446e146f221886e82d551c8fde41a95 Mon Sep 17 00:00:00 2001 From: Kile Asmussen Date: Sat, 28 Mar 2026 20:12:44 +0100 Subject: [PATCH 4/8] . --- src/report/iter.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/report/iter.rs b/src/report/iter.rs index 5793811..fe0fdc4 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -11,14 +11,14 @@ use crate::{ReportRef, markers::Dynamic, report_collection::ReportCollection}; /// manner, starting from the root report and visiting each child report before /// moving to the next sibling. #[must_use] -pub struct ReportIter<'a, Ownership: 'static, ThreadSafety: 'static, Strategy: ?Sized = DFS> { +pub struct ReportIter<'a, Ownership: 'static, ThreadSafety: 'static, Strategy = DFS> { stack: VecDeque>, _ownership: PhantomData, _thread_safety: PhantomData, - _traversal: PhantomData<*mut Strategy>, + _traversal: PhantomData, } -impl<'a, O, T, S: ?Sized> ReportIter<'a, O, T, S> { +impl<'a, O, T, S> ReportIter<'a, O, T, S> { /// Creates a new [`ReportIter`] from a vector of raw report references pub(crate) fn from_raw(stack: VecDeque>) -> Self { Self { @@ -111,16 +111,16 @@ impl<'a, O, T> ReportIter<'a, O, T, BFS> { /// Marker type for depth-first traversal in the [`ReportIter`] type. pub struct DFS { - _not_constructible: NotConstructible, + _nope: NotUserConstructible, } /// Marker type for breadth-first traversal in the [`ReportIter`] type. pub struct BFS { - _not_constructible: NotConstructible, + _nope: NotUserConstructible, } #[allow(missing_copy_implementations, reason = "not constructible")] -struct NotConstructible; +struct NotUserConstructible; fn list_children<'a, O: 'static, T>( children: &'a ReportCollection, From a8ded2511fa0efa6589766558ee230595aa5120c Mon Sep 17 00:00:00 2001 From: Kile Asmussen Date: Sat, 28 Mar 2026 20:43:35 +0100 Subject: [PATCH 5/8] final functionality --- src/lib.rs | 2 +- src/report/iter.rs | 68 +++++++++++++++++++++++++++++++--------------- src/report/mod.rs | 1 + 3 files changed, 48 insertions(+), 23 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index a5c831a..a516fe9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -356,7 +356,7 @@ pub mod preformatted; pub mod compat; pub mod option_ext; pub mod prelude; -mod report; +pub(crate) mod report; pub mod report_attachment; pub mod report_attachments; pub mod report_collection; diff --git a/src/report/iter.rs b/src/report/iter.rs index fe0fdc4..de07551 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -30,8 +30,7 @@ impl<'a, O, T, S> ReportIter<'a, O, T, S> { } } -#[cfg(test)] -fn report_tree() -> crate::Report { +pub(crate) fn generate_report_tree() -> crate::Report { use crate::report_collection::ReportCollection; use alloc::format; @@ -47,8 +46,7 @@ fn report_tree() -> crate::Report { .into_dynamic() } -#[cfg(test)] -fn join_contexts<'b, OW: 'static>( +pub(crate) fn join_contexts_as_string<'b, OW: 'static>( it: impl Iterator>, ) -> alloc::string::String { use alloc::string::ToString; @@ -69,15 +67,28 @@ impl<'a, O, T> ReportIter<'a, O, T, DFS> { /// # Examples /// /// ```rust - /// # use rootcause::ReportIter; - /// let rep = - /// # panic!("HOW THE FUCK DO I CALL MY HELPER FUNCTIONS YOU FUCKING MODULE SYSTEM"); - /// // root - /// // / \ - /// // 1 2 - /// // / \ / \ - /// // 1.1 1.2 2.1 2.2 - /// assert_eq!("", "root 1 1.1 1.2 2 2.1 2.2"); + /// # use rootcause::{report, ReportIter, report_collection::ReportCollection}; + /// let rep = /* + /// root + /// / \ + /// 1 2 + /// / \ / \ + /// 1.1 1.2 2.1 2.2 */ + /// # (1..=2).map(|i| { + /// # (1..=2) + /// # .map(|j| report!(format!("{}.{}", i, j)).into_cloneable()) + /// # .collect::() + /// # .context(format!("{}", i)) + /// # }) + /// # .collect::() + /// # .context(format!("root")).into_dynamic() + /// ; + /// assert_eq!( + /// rep.iter_reports().bfs() + /// .map(|e| e.format_current_context().to_string()) + /// .collect::>(), + /// &["root", "1", "2", "1.1", "1.2", "2.1", "2.2"] + /// ); /// ``` pub fn bfs(self) -> ReportIter<'a, O, T, BFS> { ReportIter::from_raw(self.stack) @@ -94,15 +105,28 @@ impl<'a, O, T> ReportIter<'a, O, T, BFS> { /// # Examples /// /// ```rust - /// # use rootcause::ReportIter; - /// let rep = - /// # panic!("HOW THE FUCK DO I CALL MY HELPER FUNCTIONS YOU FUCKING MODULE SYSTEM"); - /// // root - /// // / \ - /// // 1 2 - /// // / \ / \ - /// // 1.1 1.2 2.1 2.2 - /// assert_eq!("", "root 1 1.1 1.2 2 2.1 2.2"); + /// # use rootcause::{report, ReportIter, report_collection::ReportCollection}; + /// let rep = /* + /// root + /// / \ + /// 1 2 + /// / \ / \ + /// 1.1 1.2 2.1 2.2 */ + /// # (1..=2).map(|i| { + /// # (1..=2) + /// # .map(|j| report!(format!("{}.{}", i, j)).into_cloneable()) + /// # .collect::() + /// # .context(format!("{}", i)) + /// # }) + /// # .collect::() + /// # .context(format!("root")).into_dynamic() + /// ; + /// assert_eq!( + /// rep.iter_reports() + /// .map(|e| e.format_current_context().to_string()) + /// .collect::>(), + /// &["root", "1", "1.1", "1.2", "2", "2.1", "2.2"] + /// ); /// ``` pub fn dfs(self) -> ReportIter<'a, O, T, DFS> { ReportIter::from_raw(self.stack) diff --git a/src/report/mod.rs b/src/report/mod.rs index c390732..b2c52e2 100644 --- a/src/report/mod.rs +++ b/src/report/mod.rs @@ -1,3 +1,4 @@ +/// Only importable in test pub(crate) mod iter; pub(crate) mod mut_; pub(crate) mod owned; From 518ac64a5beb38106cb731bb789a7adfd085d428 Mon Sep 17 00:00:00 2001 From: Kile Asmussen Date: Thu, 21 May 2026 17:28:55 +0200 Subject: [PATCH 6/8] . --- src/report/iter.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/report/iter.rs b/src/report/iter.rs index de07551..eae88d2 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -134,17 +134,12 @@ impl<'a, O, T> ReportIter<'a, O, T, BFS> { } /// Marker type for depth-first traversal in the [`ReportIter`] type. -pub struct DFS { - _nope: NotUserConstructible, -} +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DFS; /// Marker type for breadth-first traversal in the [`ReportIter`] type. -pub struct BFS { - _nope: NotUserConstructible, -} - -#[allow(missing_copy_implementations, reason = "not constructible")] -struct NotUserConstructible; +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BFS; fn list_children<'a, O: 'static, T>( children: &'a ReportCollection, From c26e8900de3860832e830cb071bd83cf46fa781b Mon Sep 17 00:00:00 2001 From: Kile Asmussen Date: Thu, 21 May 2026 17:32:54 +0200 Subject: [PATCH 7/8] . --- src/report/iter.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/report/iter.rs b/src/report/iter.rs index 2197693..2f778e6 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -30,6 +30,7 @@ impl<'a, O, T, S> ReportIter<'a, O, T, S> { } } +#[allow(unused, reason = "doctest")] pub(crate) fn generate_report_tree() -> crate::Report { use crate::report_collection::ReportCollection; use alloc::format; @@ -46,6 +47,7 @@ pub(crate) fn generate_report_tree() -> crate::Report { .into_dynamic() } +#[allow(unused, reason = "doctest")] pub(crate) fn join_contexts_as_string<'b, OW: 'static>( it: impl Iterator>, ) -> alloc::string::String { From 6ed436031b5f7ab1ddf86cf3952f136b07a7e2d4 Mon Sep 17 00:00:00 2001 From: Tethys Svensson Date: Thu, 21 May 2026 21:57:58 +0200 Subject: [PATCH 8/8] Fix clippy errors and drop unused helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop unused `vec::Vec` import in src/report/iter.rs - Rename marker types `DFS` -> `Dfs`, `BFS` -> `Bfs` (clippy::upper_case_acronyms) - Remove dead `generate_report_tree` and `join_contexts_as_string` helpers — they had no callers and `format!("root")` triggered clippy::useless_format - Revert unrelated visibility bump `mod report` -> `pub(crate) mod report` and drop misleading "Only importable in test" doc comment --- src/lib.rs | 2 +- src/report/iter.rs | 67 +++++++++++++--------------------------------- src/report/mod.rs | 1 - 3 files changed, 19 insertions(+), 51 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index f518925..bd94210 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -355,7 +355,7 @@ pub mod markers; pub mod compat; pub mod option_ext; pub mod prelude; -pub(crate) mod report; +mod report; pub mod report_attachment; pub mod report_attachments; pub mod report_collection; diff --git a/src/report/iter.rs b/src/report/iter.rs index 3f36d2e..5c3a4ba 100644 --- a/src/report/iter.rs +++ b/src/report/iter.rs @@ -1,17 +1,15 @@ -use alloc::{collections::vec_deque::VecDeque, vec::Vec}; +use alloc::collections::vec_deque::VecDeque; use core::{iter::FusedIterator, marker::PhantomData}; use crate::{ReportRef, markers::Dynamic, report_collection::ReportCollection}; -/// An iterator over a report and all its descendant reports in depth-first -/// order. +/// An iterator over a report and all its descendant reports. /// /// This iterator yields [`ReportRef`] items, which are references to the reports -/// in the hierarchy. The iterator traverses the report tree in a depth-first -/// manner, starting from the root report and visiting each child report before -/// moving to the next sibling. +/// in the hierarchy. By default the traversal is depth-first; use +/// [`ReportIter::bfs`] to switch to breadth-first. #[must_use] -pub struct ReportIter<'a, Ownership: 'static, ThreadSafety: 'static, Strategy = DFS> { +pub struct ReportIter<'a, Ownership: 'static, ThreadSafety: 'static, Strategy = Dfs> { stack: VecDeque>, _ownership: PhantomData, _thread_safety: PhantomData, @@ -19,7 +17,7 @@ pub struct ReportIter<'a, Ownership: 'static, ThreadSafety: 'static, Strategy = } impl<'a, O, T, S> ReportIter<'a, O, T, S> { - /// Creates a new [`ReportIter`] from a vector of raw report references + /// Creates a new [`ReportIter`] from a deque of raw report references pub(crate) fn from_raw(stack: VecDeque>) -> Self { Self { stack, @@ -30,36 +28,7 @@ impl<'a, O, T, S> ReportIter<'a, O, T, S> { } } -#[allow(unused, reason = "doctest")] -pub(crate) fn generate_report_tree() -> crate::Report { - use crate::report_collection::ReportCollection; - use alloc::format; - - (1..=2) - .map(|i| { - (1..=2) - .map(|j| report!(format!("{}.{}", i, j)).into_cloneable()) - .collect::() - .context(format!("{}", i)) - }) - .collect::() - .context(format!("root")) - .into_dynamic() -} - -#[allow(unused, reason = "doctest")] -pub(crate) fn join_contexts_as_string<'b, OW: 'static>( - it: impl Iterator>, -) -> alloc::string::String { - use alloc::string::ToString; - use alloc::vec::Vec; - it.into_iter() - .map(|e: ReportRef<'_, Dynamic, OW>| e.format_current_context().to_string()) - .collect::>() - .join(" ") -} - -impl<'a, O, T> ReportIter<'a, O, T, DFS> { +impl<'a, O, T> ReportIter<'a, O, T, Dfs> { /// Convert this traversal to a breadth-first search. /// /// **Warning:** if this function is called mid-traversal, @@ -83,7 +52,7 @@ impl<'a, O, T> ReportIter<'a, O, T, DFS> { /// # .context(format!("{}", i)) /// # }) /// # .collect::() - /// # .context(format!("root")).into_dynamic() + /// # .context("root").into_dynamic() /// ; /// assert_eq!( /// rep.iter_reports().bfs() @@ -92,12 +61,12 @@ impl<'a, O, T> ReportIter<'a, O, T, DFS> { /// &["root", "1", "2", "1.1", "1.2", "2.1", "2.2"] /// ); /// ``` - pub fn bfs(self) -> ReportIter<'a, O, T, BFS> { + pub fn bfs(self) -> ReportIter<'a, O, T, Bfs> { ReportIter::from_raw(self.stack) } } -impl<'a, O, T> ReportIter<'a, O, T, BFS> { +impl<'a, O, T> ReportIter<'a, O, T, Bfs> { /// Convert this traversal to a depth-first search. /// /// **Warning:** if this function is called mid-traversal, @@ -121,7 +90,7 @@ impl<'a, O, T> ReportIter<'a, O, T, BFS> { /// # .context(format!("{}", i)) /// # }) /// # .collect::() - /// # .context(format!("root")).into_dynamic() + /// # .context("root").into_dynamic() /// ; /// assert_eq!( /// rep.iter_reports() @@ -130,18 +99,18 @@ impl<'a, O, T> ReportIter<'a, O, T, BFS> { /// &["root", "1", "1.1", "1.2", "2", "2.1", "2.2"] /// ); /// ``` - pub fn dfs(self) -> ReportIter<'a, O, T, DFS> { + pub fn dfs(self) -> ReportIter<'a, O, T, Dfs> { ReportIter::from_raw(self.stack) } } /// Marker type for depth-first traversal in the [`ReportIter`] type. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DFS; +pub struct Dfs; /// Marker type for breadth-first traversal in the [`ReportIter`] type. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct BFS; +pub struct Bfs; fn list_children<'a, O: 'static, T>( children: &'a ReportCollection, @@ -160,7 +129,7 @@ fn list_children<'a, O: 'static, T>( }) } -impl<'a, O, T> Iterator for ReportIter<'a, O, T, DFS> { +impl<'a, O, T> Iterator for ReportIter<'a, O, T, Dfs> { type Item = ReportRef<'a, Dynamic, O, T>; fn next(&mut self) -> Option { @@ -170,7 +139,7 @@ impl<'a, O, T> Iterator for ReportIter<'a, O, T, DFS> { } } -impl<'a, O, T> Iterator for ReportIter<'a, O, T, BFS> { +impl<'a, O, T> Iterator for ReportIter<'a, O, T, Bfs> { type Item = ReportRef<'a, Dynamic, O, T>; fn next(&mut self) -> Option { @@ -180,8 +149,8 @@ impl<'a, O, T> Iterator for ReportIter<'a, O, T, BFS> { } } -impl<'a, O, T> FusedIterator for ReportIter<'a, O, T, DFS> {} -impl<'a, O, T> FusedIterator for ReportIter<'a, O, T, BFS> {} +impl<'a, O, T> FusedIterator for ReportIter<'a, O, T, Dfs> {} +impl<'a, O, T> FusedIterator for ReportIter<'a, O, T, Bfs> {} impl<'a, O, T, S> Unpin for ReportIter<'a, O, T, S> {} diff --git a/src/report/mod.rs b/src/report/mod.rs index b2c52e2..c390732 100644 --- a/src/report/mod.rs +++ b/src/report/mod.rs @@ -1,4 +1,3 @@ -/// Only importable in test pub(crate) mod iter; pub(crate) mod mut_; pub(crate) mod owned;