From 8282bc7d4ad76adefba37a3a5c469c0d1e2238e3 Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 8 Dec 2025 13:31:44 +0800 Subject: [PATCH 1/5] docs: more documents and examples Signed-off-by: tison --- README.md | 71 ++++++++++++ traversable/src/combinator.rs | 4 +- traversable/src/function.rs | 199 ++++++++++++++++++++++++++++++++++ traversable/src/lib.rs | 156 ++++++++++++++++++++++++++ 4 files changed, 428 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 2088d82..a045c36 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,77 @@ This crate provides traits and proc macros to implement the visitor pattern for arbitrary data structures. This pattern is particularly useful when dealing with complex nested data structures, abstract trees and hierarchies of all kinds. +## Quick Start + +Add `traversable` to your `Cargo.toml` with the `derive` feature: + +```toml +[dependencies] +traversable = { version = "0.1", features = ["derive", "std"] } +``` + +Define your data structures and derive `Traversable`: + +```rust +use traversable::{Traversable, Visitor}; +use std::ops::ControlFlow; + +#[derive(Traversable)] +struct Directory { + name: String, + files: Vec, + #[traverse(skip)] // Do not traverse this field + cache_id: u64, +} + +#[derive(Traversable)] +struct File { + name: String, + size: u64, +} + +struct FileCounter { + count: usize, + total_size: u64, +} + +impl Visitor for FileCounter { + type Break = (); + + fn enter(&mut self, node: &dyn core::any::Any) -> ControlFlow { + if let Some(file) = node.downcast_ref::() { + self.count += 1; + self.total_size += file.size; + } + ControlFlow::Continue(()) + } +} + +fn main() { + let root = Directory { + name: "root".to_string(), + files: vec![ + File { name: "a.txt".to_string(), size: 100 }, + File { name: "b.rs".to_string(), size: 200 }, + ], + cache_id: 12345, + }; + + let mut counter = FileCounter { count: 0, total_size: 0 }; + root.traverse(&mut counter); + + assert_eq!(counter.count, 2); + assert_eq!(counter.total_size, 300); +} +``` + +## Attributes + +The derive macro supports the following attributes on fields and variants: + +* `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +* `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. + ## Minimum Rust version policy This crate is built against the latest stable release, and its minimum supported rustc version is 1.85.0. diff --git a/traversable/src/combinator.rs b/traversable/src/combinator.rs index 2476666..8ddf897 100644 --- a/traversable/src/combinator.rs +++ b/traversable/src/combinator.rs @@ -34,7 +34,7 @@ pub trait VisitorExt: Visitor { /// # /// # #[cfg(feature = "derive")] /// # fn main() { - /// use std::ops::ControlFlow; + /// use core::ops::ControlFlow; /// /// use traversable::Traversable; /// use traversable::Visitor; @@ -102,7 +102,7 @@ pub trait VisitorMutExt: VisitorMut { /// # /// # #[cfg(feature = "derive")] /// # fn main() { - /// use std::ops::ControlFlow; + /// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; diff --git a/traversable/src/function.rs b/traversable/src/function.rs index 84c42c3..ecfdbab 100644 --- a/traversable/src/function.rs +++ b/traversable/src/function.rs @@ -79,6 +79,46 @@ type DefaultVisitFn = fn(&T) -> ControlFlow; type DefaultVisitFnMut = fn(&mut T) -> ControlFlow; /// Create a visitor that only visits items of a specific type from a function or a closure. +/// Creates a new visitor from `enter` and `leave` closures. +/// +/// This is a convenience function for creating simple visitors without defining +/// a new struct and implementing the [`Visitor`] trait manually. +/// +/// # Example +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::Traversable; +/// use traversable::Visitor; +/// use traversable::function::make_visitor; +/// +/// #[cfg_attr(feature = "derive", derive(Traversable))] +/// struct Item { +/// value: i32, +/// } +/// +/// let item = Item { value: 10 }; +/// let mut total_value = 0; +/// +/// let mut visitor = make_visitor::( +/// |node: &Item| { +/// // enter closure +/// total_value += node.value; +/// ControlFlow::Continue(()) +/// }, +/// |_node: &Item| { +/// // leave closure +/// ControlFlow::Continue(()) +/// }, +/// ); +/// +/// # #[cfg(feature = "derive")] +/// # { +/// item.traverse(&mut visitor); +/// assert_eq!(total_value, 10); +/// # } +/// ``` pub fn make_visitor(enter: F1, leave: F2) -> FnVisitor where T: Any, @@ -94,6 +134,36 @@ where } /// Similar to [`make_visitor`], but the closure will only be called on entering. +/// +/// # Example +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::Traversable; +/// use traversable::Visitor; +/// use traversable::function::make_visitor_enter; +/// +/// #[cfg_attr(feature = "derive", derive(Traversable))] +/// struct Item { +/// value: i32, +/// } +/// +/// let item = Item { value: 20 }; +/// let mut count = 0; +/// +/// let mut visitor = make_visitor_enter::(|node: &Item| { +/// // enter closure +/// count += 1; +/// ControlFlow::Continue(()) +/// }); +/// +/// # #[cfg(feature = "derive")] +/// # { +/// item.traverse(&mut visitor); +/// assert_eq!(count, 1); +/// # } +/// ``` pub fn make_visitor_enter(enter: F) -> FnVisitor> where T: Any, @@ -108,6 +178,36 @@ where } /// Similar to [`make_visitor`], but the closure will only be called on leaving. +/// +/// # Example +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::Traversable; +/// use traversable::Visitor; +/// use traversable::function::make_visitor_leave; +/// +/// #[cfg_attr(feature = "derive", derive(Traversable))] +/// struct Item { +/// value: i32, +/// } +/// +/// let item = Item { value: 30 }; +/// let mut visited_leave = false; +/// +/// let mut visitor = make_visitor_leave::(|node: &Item| { +/// // leave closure +/// visited_leave = true; +/// ControlFlow::Continue(()) +/// }); +/// +/// # #[cfg(feature = "derive")] +/// # { +/// item.traverse(&mut visitor); +/// assert!(visited_leave); +/// # } +/// ``` pub fn make_visitor_leave(leave: F) -> FnVisitor, F> where T: Any, @@ -122,6 +222,45 @@ where } /// Create a visitor that only visits mutable items of a specific type from a function or a closure. +/// Creates a new mutable visitor from `enter` and `leave` closures. +/// +/// This is a convenience function for creating simple mutable visitors without defining +/// a new struct and implementing the [`VisitorMut`] trait manually. +/// +/// # Example +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::TraversableMut; +/// use traversable::VisitorMut; +/// use traversable::function::make_visitor_mut; +/// +/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +/// struct Item { +/// value: i32, +/// } +/// +/// let mut item = Item { value: 10 }; +/// +/// let mut visitor = make_visitor_mut::( +/// |node: &mut Item| { +/// // enter_mut closure +/// node.value += 1; +/// ControlFlow::Continue(()) +/// }, +/// |_node: &mut Item| { +/// // leave_mut closure +/// ControlFlow::Continue(()) +/// }, +/// ); +/// +/// # #[cfg(feature = "derive")] +/// # { +/// item.traverse_mut(&mut visitor); +/// assert_eq!(item.value, 11); +/// # } +/// ``` pub fn make_visitor_mut(enter: F1, leave: F2) -> FnVisitor where T: Any, @@ -137,6 +276,36 @@ where } /// Similar to [`make_visitor_mut`], but the closure will only be called on entering. +/// +/// # Example +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::TraversableMut; +/// use traversable::VisitorMut; +/// use traversable::function::make_visitor_enter_mut; +/// +/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +/// struct Item { +/// value: i32, +/// } +/// +/// let mut item = Item { value: 20 }; +/// let mut count = 0; +/// +/// let mut visitor = make_visitor_enter_mut::(|node: &mut Item| { +/// // enter_mut closure +/// count += 1; +/// ControlFlow::Continue(()) +/// }); +/// +/// # #[cfg(feature = "derive")] +/// # { +/// item.traverse_mut(&mut visitor); +/// assert_eq!(count, 1); +/// # } +/// ``` pub fn make_visitor_enter_mut(enter: F) -> FnVisitor> where T: Any, @@ -151,6 +320,36 @@ where } /// Similar to [`make_visitor_mut`], but the closure will only be called on leaving. +/// +/// # Example +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::TraversableMut; +/// use traversable::VisitorMut; +/// use traversable::function::make_visitor_leave_mut; +/// +/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +/// struct Item { +/// value: i32, +/// } +/// +/// let mut item = Item { value: 30 }; +/// let mut visited_leave = false; +/// +/// let mut visitor = make_visitor_leave_mut::(|node: &mut Item| { +/// // leave_mut closure +/// visited_leave = true; +/// ControlFlow::Continue(()) +/// }); +/// +/// # #[cfg(feature = "derive")] +/// # { +/// item.traverse_mut(&mut visitor); +/// assert!(visited_leave); +/// # } +/// ``` pub fn make_visitor_leave_mut(leave: F) -> FnVisitor, F> where T: Any, diff --git a/traversable/src/lib.rs b/traversable/src/lib.rs index 0bc2992..3d28cb3 100644 --- a/traversable/src/lib.rs +++ b/traversable/src/lib.rs @@ -12,7 +12,111 @@ // See the License for the specific language governing permissions and // limitations under the License. +//! # Traversable +//! //! A visitor pattern implementation for traversing data structures. +//! +//! This crate provides a [`Traversable`] trait (and [`TraversableMut`]) for types that can be +//! traversed, and a [`Visitor`] trait (and [`VisitorMut`]) for types that perform the traversal. +//! +//! It is designed to be flexible and efficient, allowing for deep traversal of complex data +//! structures. +//! +//! ## Quick Start +//! +//! Add `traversable` to your `Cargo.toml` with the `derive` feature: +//! +//! ```toml +//! [dependencies] +//! traversable = { version = "0.1", features = ["derive", "std"] } +//! ``` +//! +//! Define your data structures and derive [`Traversable`]: +//! +//! ```rust +//! # #[cfg(not(all(feature = "derive", feature = "std")))] +//! # fn main() {} +//! # +//! # #[cfg(all(feature = "derive", feature = "std"))] +//! # fn main() { +//! use std::ops::ControlFlow; +//! use std::any::Any; +//! +//! use traversable::Traversable; +//! use traversable::Visitor; +//! +//! #[derive(Traversable)] +//! struct Directory { +//! name: String, +//! files: Vec, +//! #[traverse(skip)] // skip traverse this field +//! cache_id: u64, +//! } +//! +//! #[derive(Traversable)] +//! struct File { +//! name: String, +//! size: u64, +//! } +//! +//! struct FileCounter { +//! count: usize, +//! total_size: u64, +//! } +//! +//! impl Visitor for FileCounter { +//! type Break = (); +//! +//! fn enter(&mut self, node: &dyn Any) -> ControlFlow { +//! if let Some(file) = node.downcast_ref::() { +//! self.count += 1; +//! self.total_size += file.size; +//! } +//! ControlFlow::Continue(()) +//! } +//! } +//! +//! let root = Directory { +//! name: "root".to_string(), +//! files: vec![ +//! File { +//! name: "a.txt".to_string(), +//! size: 100, +//! }, +//! File { +//! name: "b.rs".to_string(), +//! size: 200, +//! }, +//! ], +//! cache_id: 12345, +//! }; +//! +//! let mut counter = FileCounter { +//! count: 0, +//! total_size: 0, +//! }; +//! root.traverse(&mut counter); +//! +//! assert_eq!(counter.count, 2); +//! assert_eq!(counter.total_size, 300); +//! # } +//! ``` +//! +//! ## Attributes +//! +//! The derive macro supports the following attributes on fields and variants: +//! +//! * `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +//! * `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. +//! +//! ## Features +//! +//! * `derive`: Enables procedural macros `#[derive(Traversable)]` and `#[derive(TraversableMut)]`. +//! * `std`: Enables support for standard library types (e.g., `Vec`, `HashMap`, `Box`). +//! * `traverse-trivial`: Enables traversal for primitive types (`u8`, `i32`, `bool`, etc.). By +//! default, these are ignored. +//! * `traverse-std`: Enables traversal for "primary" std types like `String`. By default, these are +//! ignored. #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(missing_docs)] @@ -37,6 +141,15 @@ pub mod function; mod impls; /// A visitor that can be used to traverse a data structure. +/// +/// Implement this trait to define custom logic that executes when +/// [`Traversable`] items are visited. You can implement `enter` and `leave` +/// methods to perform actions before and after processing a node, respectively. +/// +/// For an example of implementing `Visitor`, see the [`FileCounter`] struct +/// in the crate-level documentation. +/// +/// You can also use [`make_visitor`] to create a visitor from closures. pub trait Visitor { /// The type that can be used to break traversal early. type Break; @@ -59,6 +172,49 @@ pub trait Visitor { } /// A visitor that can be used to traverse a mutable data structure. +/// +/// Implement this trait to define custom logic that executes when +/// [`TraversableMut`] items are visited. You can implement `enter_mut` and `leave_mut` +/// methods to perform actions before and after processing a mutable node, respectively. +/// +/// # Example +/// +/// ```rust +/// use core::any::Any; +/// use core::ops::ControlFlow; +/// +/// use traversable::TraversableMut; +/// use traversable::VisitorMut; +/// +/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +/// struct Node { +/// value: i32, +/// #[cfg_attr(feature = "derive", traverse(skip))] +/// id: u32, +/// } +/// +/// struct Incrementer; +/// +/// impl VisitorMut for Incrementer { +/// type Break = (); +/// +/// fn enter_mut(&mut self, node: &mut dyn Any) -> ControlFlow { +/// if let Some(n) = node.downcast_mut::() { +/// n.value += 1; +/// } +/// ControlFlow::Continue(()) +/// } +/// } +/// +/// # #[cfg(feature = "derive")] +/// # { +/// let mut node = Node { value: 10, id: 1 }; +/// node.traverse_mut(&mut Incrementer); +/// assert_eq!(node.value, 11); +/// # } +/// ``` +/// +/// You can also use [`make_visitor_mut`] to create a mutable visitor from closures. pub trait VisitorMut { /// The type that can be used to break traversal early. type Break; From 649ef2a3077e5282318a6b43b56fae7af0ba2b47 Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 8 Dec 2025 14:05:06 +0800 Subject: [PATCH 2/5] fixup Signed-off-by: tison --- traversable/src/combinator.rs | 32 +++------ traversable/src/function.rs | 18 ++--- traversable/src/lib.rs | 132 ++++++++++++++++++++++++++++++++-- 3 files changed, 142 insertions(+), 40 deletions(-) diff --git a/traversable/src/combinator.rs b/traversable/src/combinator.rs index 8ddf897..7e661f4 100644 --- a/traversable/src/combinator.rs +++ b/traversable/src/combinator.rs @@ -29,25 +29,17 @@ pub trait VisitorExt: Visitor { /// # Examples /// /// ``` - /// # #[cfg(not(feature = "derive"))] - /// # fn main() {} - /// # - /// # #[cfg(feature = "derive")] - /// # fn main() { /// use core::ops::ControlFlow; /// /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::combinator::VisitorExt; /// use traversable::function::make_visitor_enter; - /// - /// #[derive(Traversable)] + #[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] /// struct Foo(i32); - /// - /// #[derive(Traversable)] + #[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] /// struct Bar(i32); - /// - /// #[derive(Traversable)] + #[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] /// struct Data { /// foo: Foo, /// bar: Bar, @@ -68,6 +60,8 @@ pub trait VisitorExt: Visitor { /// ControlFlow::<()>::Continue(()) /// }); /// + /// # #[cfg(feature = "derive")] + /// # { /// // v1 runs first, then v2. /// let mut combined = v1.or(v2); /// data.traverse(&mut combined); @@ -97,25 +91,17 @@ pub trait VisitorMutExt: VisitorMut { /// # Examples /// /// ``` - /// # #[cfg(not(feature = "derive"))] - /// # fn main() {} - /// # - /// # #[cfg(feature = "derive")] - /// # fn main() { /// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::combinator::VisitorMutExt; /// use traversable::function::make_visitor_enter_mut; - /// - /// #[derive(TraversableMut)] + #[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] /// struct Foo(i32); - /// - /// #[derive(TraversableMut)] + #[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] /// struct Bar(i32); - /// - /// #[derive(TraversableMut)] + #[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] /// struct Data { /// foo: Foo, /// bar: Bar, @@ -136,6 +122,8 @@ pub trait VisitorMutExt: VisitorMut { /// ControlFlow::<()>::Continue(()) /// }); /// + /// # #[cfg(feature = "derive")] + /// # { /// let mut combined = v1.or(v2); /// data.traverse_mut(&mut combined); /// diff --git a/traversable/src/function.rs b/traversable/src/function.rs index ecfdbab..c33becb 100644 --- a/traversable/src/function.rs +++ b/traversable/src/function.rs @@ -92,8 +92,7 @@ type DefaultVisitFnMut = fn(&mut T) -> ControlFlow; /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::function::make_visitor; -/// -/// #[cfg_attr(feature = "derive", derive(Traversable))] +#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] /// struct Item { /// value: i32, /// } @@ -143,8 +142,7 @@ where /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::function::make_visitor_enter; -/// -/// #[cfg_attr(feature = "derive", derive(Traversable))] +#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] /// struct Item { /// value: i32, /// } @@ -187,8 +185,7 @@ where /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::function::make_visitor_leave; -/// -/// #[cfg_attr(feature = "derive", derive(Traversable))] +#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] /// struct Item { /// value: i32, /// } @@ -235,8 +232,7 @@ where /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::function::make_visitor_mut; -/// -/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] /// struct Item { /// value: i32, /// } @@ -285,8 +281,7 @@ where /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::function::make_visitor_enter_mut; -/// -/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] /// struct Item { /// value: i32, /// } @@ -329,8 +324,7 @@ where /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::function::make_visitor_leave_mut; -/// -/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] /// struct Item { /// value: i32, /// } diff --git a/traversable/src/lib.rs b/traversable/src/lib.rs index 3d28cb3..18c0642 100644 --- a/traversable/src/lib.rs +++ b/traversable/src/lib.rs @@ -39,8 +39,8 @@ //! # //! # #[cfg(all(feature = "derive", feature = "std"))] //! # fn main() { -//! use std::ops::ControlFlow; //! use std::any::Any; +//! use std::ops::ControlFlow; //! //! use traversable::Traversable; //! use traversable::Visitor; @@ -146,10 +146,12 @@ mod impls; /// [`Traversable`] items are visited. You can implement `enter` and `leave` /// methods to perform actions before and after processing a node, respectively. /// -/// For an example of implementing `Visitor`, see the [`FileCounter`] struct -/// in the crate-level documentation. +/// For an example of implementing `Visitor`, see the `FileCounter` struct +/// in the [crate-level documentation](self). /// /// You can also use [`make_visitor`] to create a visitor from closures. +/// +/// [`make_visitor`]: function::make_visitor pub trait Visitor { /// The type that can be used to break traversal early. type Break; @@ -185,11 +187,10 @@ pub trait Visitor { /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; -/// -/// #[cfg_attr(feature = "derive", derive(TraversableMut))] +#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] /// struct Node { /// value: i32, -/// #[cfg_attr(feature = "derive", traverse(skip))] +#[cfg_attr(feature = "derive", doc = " #[traverse(skip)]")] /// id: u32, /// } /// @@ -215,6 +216,8 @@ pub trait Visitor { /// ``` /// /// You can also use [`make_visitor_mut`] to create a mutable visitor from closures. +/// +/// [`make_visitor_mut`]: function::make_visitor_mut pub trait VisitorMut { /// The type that can be used to break traversal early. type Break; @@ -237,12 +240,129 @@ pub trait VisitorMut { } /// A trait for types that can be traversed by a visitor. +/// +/// This trait is the core of the traversable pattern. It allows a [`Visitor`] to +/// walk through a data structure. +/// +/// # Deriving `Traversable` +/// +/// The easiest way to implement `Traversable` is to use the `derive` macro. +/// +/// ```rust +/// use traversable::Traversable; +/// +#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] +/// struct MyStruct { +/// data: u64, +#[cfg_attr( + feature = "derive", + doc = r#" #[traverse(skip)] // skip this field"# +)] +/// hidden: String, +/// } +/// ``` +/// +/// # Attributes +/// +/// The derive macro supports the following attributes: +/// +/// * `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +/// * `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. +/// +/// ## Custom Traversal Function +/// +/// When using `#[traverse(with = "path::to::func")]`, the function must have the signature: +/// +/// ```rust,ignore +/// fn func(item: &ItemType, visitor: &mut V) -> ControlFlow +/// ``` +/// +/// Example: +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::Traversable; +/// use traversable::Visitor; +/// +/// fn traverse_string_len(s: &String, visitor: &mut V) -> ControlFlow { +/// s.len().traverse(visitor) +/// } +/// +#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] +/// struct User { +#[cfg_attr( + feature = "derive", + doc = r#" #[traverse(with = "traverse_string_len")]"# +)] +/// name: String, +/// } +/// ``` pub trait Traversable: core::any::Any { /// Traverse the data structure with the given visitor. fn traverse(&self, visitor: &mut V) -> ControlFlow; } /// A trait for types that can be traversed mutably by a visitor. +/// +/// This trait allows a [`VisitorMut`] to walk through a data structure and possibly +/// mutate it. +/// +/// # Deriving `TraversableMut` +/// +/// The easiest way to implement `TraversableMut` is to use the `derive` macro. +/// +/// ```rust +/// use traversable::TraversableMut; +/// +#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// struct MyStruct { +/// data: u64, +#[cfg_attr(feature = "derive", doc = " #[traverse(skip)] // skip this field")] +/// readonly: String, +/// } +/// ``` +/// +/// # Attributes +/// +/// The derive macro supports the following attributes: +/// +/// * `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +/// * `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. +/// +/// ## Custom Traversal Function +/// +/// When using `#[traverse(with = "path::to::func")]`, the function must have the signature: +/// +/// ```rust,ignore +/// fn func(item: &mut ItemType, visitor: &mut V) -> ControlFlow +/// ``` +/// +/// Example: +/// +/// ```rust +/// use std::ops::ControlFlow; +/// +/// use traversable::TraversableMut; +/// use traversable::VisitorMut; +/// +/// fn traverse_string_chars( +/// s: &mut String, +/// visitor: &mut V, +/// ) -> ControlFlow { +/// // custom traversal logic +/// ControlFlow::Continue(()) +/// } +/// +#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// struct User { +#[cfg_attr( + feature = "derive", + doc = r#" #[traverse(with = "traverse_string_chars")]"# +)] +/// name: String, +/// } +/// ``` pub trait TraversableMut: core::any::Any { /// Traverse the mutable data structure with the given visitor. fn traverse_mut(&mut self, visitor: &mut V) -> ControlFlow; From bf974c59fe8ea7e1153d725ac5e098009222f41a Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 8 Dec 2025 14:13:43 +0800 Subject: [PATCH 3/5] fixup Signed-off-by: tison --- traversable/src/combinator.rs | 32 +++++++++++----- traversable/src/function.rs | 72 +++++++++++++++++++++++------------ traversable/src/lib.rs | 64 ++++++++++++++++++++----------- 3 files changed, 111 insertions(+), 57 deletions(-) diff --git a/traversable/src/combinator.rs b/traversable/src/combinator.rs index 7e661f4..8ddf897 100644 --- a/traversable/src/combinator.rs +++ b/traversable/src/combinator.rs @@ -29,17 +29,25 @@ pub trait VisitorExt: Visitor { /// # Examples /// /// ``` + /// # #[cfg(not(feature = "derive"))] + /// # fn main() {} + /// # + /// # #[cfg(feature = "derive")] + /// # fn main() { /// use core::ops::ControlFlow; /// /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::combinator::VisitorExt; /// use traversable::function::make_visitor_enter; - #[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] + /// + /// #[derive(Traversable)] /// struct Foo(i32); - #[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] + /// + /// #[derive(Traversable)] /// struct Bar(i32); - #[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] + /// + /// #[derive(Traversable)] /// struct Data { /// foo: Foo, /// bar: Bar, @@ -60,8 +68,6 @@ pub trait VisitorExt: Visitor { /// ControlFlow::<()>::Continue(()) /// }); /// - /// # #[cfg(feature = "derive")] - /// # { /// // v1 runs first, then v2. /// let mut combined = v1.or(v2); /// data.traverse(&mut combined); @@ -91,17 +97,25 @@ pub trait VisitorMutExt: VisitorMut { /// # Examples /// /// ``` + /// # #[cfg(not(feature = "derive"))] + /// # fn main() {} + /// # + /// # #[cfg(feature = "derive")] + /// # fn main() { /// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::combinator::VisitorMutExt; /// use traversable::function::make_visitor_enter_mut; - #[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] + /// + /// #[derive(TraversableMut)] /// struct Foo(i32); - #[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] + /// + /// #[derive(TraversableMut)] /// struct Bar(i32); - #[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] + /// + /// #[derive(TraversableMut)] /// struct Data { /// foo: Foo, /// bar: Bar, @@ -122,8 +136,6 @@ pub trait VisitorMutExt: VisitorMut { /// ControlFlow::<()>::Continue(()) /// }); /// - /// # #[cfg(feature = "derive")] - /// # { /// let mut combined = v1.or(v2); /// data.traverse_mut(&mut combined); /// diff --git a/traversable/src/function.rs b/traversable/src/function.rs index c33becb..58ce3c7 100644 --- a/traversable/src/function.rs +++ b/traversable/src/function.rs @@ -87,12 +87,18 @@ type DefaultVisitFnMut = fn(&mut T) -> ControlFlow; /// # Example /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::function::make_visitor; -#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] +/// +/// #[derive(Traversable)] /// struct Item { /// value: i32, /// } @@ -112,8 +118,6 @@ type DefaultVisitFnMut = fn(&mut T) -> ControlFlow; /// }, /// ); /// -/// # #[cfg(feature = "derive")] -/// # { /// item.traverse(&mut visitor); /// assert_eq!(total_value, 10); /// # } @@ -137,12 +141,18 @@ where /// # Example /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::function::make_visitor_enter; -#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] +/// +/// #[derive(Traversable)] /// struct Item { /// value: i32, /// } @@ -156,8 +166,6 @@ where /// ControlFlow::Continue(()) /// }); /// -/// # #[cfg(feature = "derive")] -/// # { /// item.traverse(&mut visitor); /// assert_eq!(count, 1); /// # } @@ -180,12 +188,18 @@ where /// # Example /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::Traversable; /// use traversable::Visitor; /// use traversable::function::make_visitor_leave; -#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] +/// +/// #[derive(Traversable)] /// struct Item { /// value: i32, /// } @@ -199,8 +213,6 @@ where /// ControlFlow::Continue(()) /// }); /// -/// # #[cfg(feature = "derive")] -/// # { /// item.traverse(&mut visitor); /// assert!(visited_leave); /// # } @@ -227,12 +239,18 @@ where /// # Example /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::function::make_visitor_mut; -#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// +/// #[derive(TraversableMut)] /// struct Item { /// value: i32, /// } @@ -251,8 +269,6 @@ where /// }, /// ); /// -/// # #[cfg(feature = "derive")] -/// # { /// item.traverse_mut(&mut visitor); /// assert_eq!(item.value, 11); /// # } @@ -276,12 +292,18 @@ where /// # Example /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::function::make_visitor_enter_mut; -#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// +/// #[derive(TraversableMut)] /// struct Item { /// value: i32, /// } @@ -295,8 +317,6 @@ where /// ControlFlow::Continue(()) /// }); /// -/// # #[cfg(feature = "derive")] -/// # { /// item.traverse_mut(&mut visitor); /// assert_eq!(count, 1); /// # } @@ -319,12 +339,18 @@ where /// # Example /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; /// use traversable::function::make_visitor_leave_mut; -#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// +/// #[derive(TraversableMut)] /// struct Item { /// value: i32, /// } @@ -338,8 +364,6 @@ where /// ControlFlow::Continue(()) /// }); /// -/// # #[cfg(feature = "derive")] -/// # { /// item.traverse_mut(&mut visitor); /// assert!(visited_leave); /// # } diff --git a/traversable/src/lib.rs b/traversable/src/lib.rs index 18c0642..d0ffb0a 100644 --- a/traversable/src/lib.rs +++ b/traversable/src/lib.rs @@ -182,15 +182,20 @@ pub trait Visitor { /// # Example /// /// ```rust +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { /// use core::any::Any; /// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; -#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// #[derive(TraversableMut)] /// struct Node { /// value: i32, -#[cfg_attr(feature = "derive", doc = " #[traverse(skip)]")] +/// #[traverse(skip)] /// id: u32, /// } /// @@ -207,8 +212,6 @@ pub trait Visitor { /// } /// } /// -/// # #[cfg(feature = "derive")] -/// # { /// let mut node = Node { value: 10, id: 1 }; /// node.traverse_mut(&mut Incrementer); /// assert_eq!(node.value, 11); @@ -249,17 +252,20 @@ pub trait VisitorMut { /// The easiest way to implement `Traversable` is to use the `derive` macro. /// /// ```rust +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { /// use traversable::Traversable; /// -#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] +/// #[derive(Traversable)] /// struct MyStruct { /// data: u64, -#[cfg_attr( - feature = "derive", - doc = r#" #[traverse(skip)] // skip this field"# -)] +/// #[traverse(skip)] // skip this field /// hidden: String, /// } +/// # } /// ``` /// /// # Attributes @@ -280,7 +286,12 @@ pub trait VisitorMut { /// Example: /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::Traversable; /// use traversable::Visitor; @@ -289,14 +300,12 @@ pub trait VisitorMut { /// s.len().traverse(visitor) /// } /// -#[cfg_attr(feature = "derive", doc = "#[derive(Traversable)]")] +/// #[derive(Traversable)] /// struct User { -#[cfg_attr( - feature = "derive", - doc = r#" #[traverse(with = "traverse_string_len")]"# -)] +/// #[traverse(with = "traverse_string_len")] /// name: String, /// } +/// # } /// ``` pub trait Traversable: core::any::Any { /// Traverse the data structure with the given visitor. @@ -313,14 +322,20 @@ pub trait Traversable: core::any::Any { /// The easiest way to implement `TraversableMut` is to use the `derive` macro. /// /// ```rust +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { /// use traversable::TraversableMut; /// -#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// #[derive(TraversableMut)] /// struct MyStruct { /// data: u64, -#[cfg_attr(feature = "derive", doc = " #[traverse(skip)] // skip this field")] +/// #[traverse(skip)] // skip this field /// readonly: String, /// } +/// # } /// ``` /// /// # Attributes @@ -341,7 +356,12 @@ pub trait Traversable: core::any::Any { /// Example: /// /// ```rust -/// use std::ops::ControlFlow; +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; /// /// use traversable::TraversableMut; /// use traversable::VisitorMut; @@ -354,14 +374,12 @@ pub trait Traversable: core::any::Any { /// ControlFlow::Continue(()) /// } /// -#[cfg_attr(feature = "derive", doc = "#[derive(TraversableMut)]")] +/// #[derive(TraversableMut)] /// struct User { -#[cfg_attr( - feature = "derive", - doc = r#" #[traverse(with = "traverse_string_chars")]"# -)] +/// #[traverse(with = "traverse_string_chars")] /// name: String, /// } +/// # } /// ``` pub trait TraversableMut: core::any::Any { /// Traverse the mutable data structure with the given visitor. From 9a5eed60852553bcfa5255642ffa718e9b378b53 Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 8 Dec 2025 14:29:09 +0800 Subject: [PATCH 4/5] address comments Signed-off-by: tison --- README.md | 8 +++++--- traversable/src/function.rs | 7 +++---- traversable/src/lib.rs | 20 +++++++++++--------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index a045c36..6dd0046 100644 --- a/README.md +++ b/README.md @@ -32,14 +32,16 @@ traversable = { version = "0.1", features = ["derive", "std"] } Define your data structures and derive `Traversable`: ```rust -use traversable::{Traversable, Visitor}; use std::ops::ControlFlow; +use traversable::Traversable; +use traversable::Visitor; + #[derive(Traversable)] struct Directory { name: String, files: Vec, - #[traverse(skip)] // Do not traverse this field + #[traverse(skip)] cache_id: u64, } @@ -88,7 +90,7 @@ fn main() { The derive macro supports the following attributes on fields and variants: -* `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +* `#[traverse(skip)]`: Skips traversing into the annotated field or variant. * `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. ## Minimum Rust version policy diff --git a/traversable/src/function.rs b/traversable/src/function.rs index 58ce3c7..c25644b 100644 --- a/traversable/src/function.rs +++ b/traversable/src/function.rs @@ -78,8 +78,7 @@ where type DefaultVisitFn = fn(&T) -> ControlFlow; type DefaultVisitFnMut = fn(&mut T) -> ControlFlow; -/// Create a visitor that only visits items of a specific type from a function or a closure. -/// Creates a new visitor from `enter` and `leave` closures. +/// Create a visitor that only visits items of a specific type from `enter` and `leave` closures. /// /// This is a convenience function for creating simple visitors without defining /// a new struct and implementing the [`Visitor`] trait manually. @@ -230,8 +229,8 @@ where } } -/// Create a visitor that only visits mutable items of a specific type from a function or a closure. -/// Creates a new mutable visitor from `enter` and `leave` closures. +/// Create a visitor that only visits mutable items of a specific type from `enter` and `leave` +/// closures. /// /// This is a convenience function for creating simple mutable visitors without defining /// a new struct and implementing the [`VisitorMut`] trait manually. diff --git a/traversable/src/lib.rs b/traversable/src/lib.rs index d0ffb0a..d41f276 100644 --- a/traversable/src/lib.rs +++ b/traversable/src/lib.rs @@ -16,8 +16,9 @@ //! //! A visitor pattern implementation for traversing data structures. //! -//! This crate provides a [`Traversable`] trait (and [`TraversableMut`]) for types that can be -//! traversed, and a [`Visitor`] trait (and [`VisitorMut`]) for types that perform the traversal. +//! This crate provides [`Traversable`] and [`TraversableMut`] trait for types that can be +//! traversed, as well as [`Visitor`] and [`VisitorMut`] trait for types that perform the +//! traversal. //! //! It is designed to be flexible and efficient, allowing for deep traversal of complex data //! structures. @@ -49,7 +50,7 @@ //! struct Directory { //! name: String, //! files: Vec, -//! #[traverse(skip)] // skip traverse this field +//! #[traverse(skip)] //! cache_id: u64, //! } //! @@ -106,7 +107,7 @@ //! //! The derive macro supports the following attributes on fields and variants: //! -//! * `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +//! * `#[traverse(skip)]`: Skips traversing into the annotated field or variant. //! * `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. //! //! ## Features @@ -116,7 +117,8 @@ //! * `traverse-trivial`: Enables traversal for primitive types (`u8`, `i32`, `bool`, etc.). By //! default, these are ignored. //! * `traverse-std`: Enables traversal for "primary" std types like `String`. By default, these are -//! ignored. +//! ignored. Note that container types like `Vec` are always traversed if the `std` feature is +//! enabled. #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(missing_docs)] @@ -262,7 +264,7 @@ pub trait VisitorMut { /// #[derive(Traversable)] /// struct MyStruct { /// data: u64, -/// #[traverse(skip)] // skip this field +/// #[traverse(skip)] /// hidden: String, /// } /// # } @@ -272,7 +274,7 @@ pub trait VisitorMut { /// /// The derive macro supports the following attributes: /// -/// * `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +/// * `#[traverse(skip)]`: Skips traversing into the annotated field or variant. /// * `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. /// /// ## Custom Traversal Function @@ -332,7 +334,7 @@ pub trait Traversable: core::any::Any { /// #[derive(TraversableMut)] /// struct MyStruct { /// data: u64, -/// #[traverse(skip)] // skip this field +/// #[traverse(skip)] /// readonly: String, /// } /// # } @@ -342,7 +344,7 @@ pub trait Traversable: core::any::Any { /// /// The derive macro supports the following attributes: /// -/// * `#[traverse(skip)]`: Skips traversal of the annotated field or variant. +/// * `#[traverse(skip)]`: Skips traversing into the annotated field or variant. /// * `#[traverse(with = "function_name")]`: Uses a custom function to traverse the field. /// /// ## Custom Traversal Function From 32334b7ad5c3a268906a0a30b67483793aa66789 Mon Sep 17 00:00:00 2001 From: tison Date: Mon, 8 Dec 2025 14:38:08 +0800 Subject: [PATCH 5/5] comments Signed-off-by: tison --- README.md | 3 ++- traversable/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6dd0046..88bd70a 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,7 @@ traversable = { version = "0.1", features = ["derive", "std"] } Define your data structures and derive `Traversable`: ```rust +use std::any::Any; use std::ops::ControlFlow; use traversable::Traversable; @@ -59,7 +60,7 @@ struct FileCounter { impl Visitor for FileCounter { type Break = (); - fn enter(&mut self, node: &dyn core::any::Any) -> ControlFlow { + fn enter(&mut self, node: &dyn Any) -> ControlFlow { if let Some(file) = node.downcast_ref::() { self.count += 1; self.total_size += file.size; diff --git a/traversable/src/lib.rs b/traversable/src/lib.rs index d41f276..d482b24 100644 --- a/traversable/src/lib.rs +++ b/traversable/src/lib.rs @@ -16,8 +16,8 @@ //! //! A visitor pattern implementation for traversing data structures. //! -//! This crate provides [`Traversable`] and [`TraversableMut`] trait for types that can be -//! traversed, as well as [`Visitor`] and [`VisitorMut`] trait for types that perform the +//! This crate provides [`Traversable`] and [`TraversableMut`] traits for types that can be +//! traversed, as well as [`Visitor`] and [`VisitorMut`] traits for types that perform the //! traversal. //! //! It is designed to be flexible and efficient, allowing for deep traversal of complex data