diff --git a/README.md b/README.md index 2088d82..88bd70a 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,80 @@ 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 std::any::Any; +use std::ops::ControlFlow; + +use traversable::Traversable; +use traversable::Visitor; + +#[derive(Traversable)] +struct Directory { + name: String, + files: Vec, + #[traverse(skip)] + 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(()) + } +} + +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 traversing into 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..c25644b 100644 --- a/traversable/src/function.rs +++ b/traversable/src/function.rs @@ -78,7 +78,49 @@ 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. +/// 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. +/// +/// # Example +/// +/// ```rust +/// # #[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; +/// +/// #[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(()) +/// }, +/// ); +/// +/// item.traverse(&mut visitor); +/// assert_eq!(total_value, 10); +/// # } +/// ``` pub fn make_visitor(enter: F1, leave: F2) -> FnVisitor where T: Any, @@ -94,6 +136,39 @@ where } /// Similar to [`make_visitor`], but the closure will only be called on entering. +/// +/// # Example +/// +/// ```rust +/// # #[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; +/// +/// #[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(()) +/// }); +/// +/// item.traverse(&mut visitor); +/// assert_eq!(count, 1); +/// # } +/// ``` pub fn make_visitor_enter(enter: F) -> FnVisitor> where T: Any, @@ -108,6 +183,39 @@ where } /// Similar to [`make_visitor`], but the closure will only be called on leaving. +/// +/// # Example +/// +/// ```rust +/// # #[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; +/// +/// #[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(()) +/// }); +/// +/// item.traverse(&mut visitor); +/// assert!(visited_leave); +/// # } +/// ``` pub fn make_visitor_leave(leave: F) -> FnVisitor, F> where T: Any, @@ -121,7 +229,49 @@ where } } -/// Create a visitor that only visits mutable items of a specific type from a function or a closure. +/// 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. +/// +/// # Example +/// +/// ```rust +/// # #[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; +/// +/// #[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(()) +/// }, +/// ); +/// +/// 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 +287,39 @@ where } /// Similar to [`make_visitor_mut`], but the closure will only be called on entering. +/// +/// # Example +/// +/// ```rust +/// # #[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; +/// +/// #[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(()) +/// }); +/// +/// item.traverse_mut(&mut visitor); +/// assert_eq!(count, 1); +/// # } +/// ``` pub fn make_visitor_enter_mut(enter: F) -> FnVisitor> where T: Any, @@ -151,6 +334,39 @@ where } /// Similar to [`make_visitor_mut`], but the closure will only be called on leaving. +/// +/// # Example +/// +/// ```rust +/// # #[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; +/// +/// #[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(()) +/// }); +/// +/// 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..d482b24 100644 --- a/traversable/src/lib.rs +++ b/traversable/src/lib.rs @@ -12,7 +12,113 @@ // 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 [`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 +//! 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::any::Any; +//! use std::ops::ControlFlow; +//! +//! use traversable::Traversable; +//! use traversable::Visitor; +//! +//! #[derive(Traversable)] +//! struct Directory { +//! name: String, +//! files: Vec, +//! #[traverse(skip)] +//! 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 traversing into 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. Note that container types like `Vec` are always traversed if the `std` feature is +//! enabled. #![cfg_attr(docsrs, feature(doc_cfg))] #![deny(missing_docs)] @@ -37,6 +143,17 @@ 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](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; @@ -59,6 +176,53 @@ 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 +/// # #[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; +/// #[derive(TraversableMut)] +/// struct Node { +/// value: i32, +/// #[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(()) +/// } +/// } +/// +/// 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. +/// +/// [`make_visitor_mut`]: function::make_visitor_mut pub trait VisitorMut { /// The type that can be used to break traversal early. type Break; @@ -81,12 +245,144 @@ 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 +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use traversable::Traversable; +/// +/// #[derive(Traversable)] +/// struct MyStruct { +/// data: u64, +/// #[traverse(skip)] +/// hidden: String, +/// } +/// # } +/// ``` +/// +/// # Attributes +/// +/// The derive macro supports the following attributes: +/// +/// * `#[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 +/// +/// 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 +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; +/// +/// use traversable::Traversable; +/// use traversable::Visitor; +/// +/// fn traverse_string_len(s: &String, visitor: &mut V) -> ControlFlow { +/// s.len().traverse(visitor) +/// } +/// +/// #[derive(Traversable)] +/// struct User { +/// #[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 +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use traversable::TraversableMut; +/// +/// #[derive(TraversableMut)] +/// struct MyStruct { +/// data: u64, +/// #[traverse(skip)] +/// readonly: String, +/// } +/// # } +/// ``` +/// +/// # Attributes +/// +/// The derive macro supports the following attributes: +/// +/// * `#[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 +/// +/// 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 +/// # #[cfg(not(feature = "derive"))] +/// # fn main() {} +/// # +/// # #[cfg(feature = "derive")] +/// # fn main() { +/// use core::ops::ControlFlow; +/// +/// use traversable::TraversableMut; +/// use traversable::VisitorMut; +/// +/// fn traverse_string_chars( +/// s: &mut String, +/// visitor: &mut V, +/// ) -> ControlFlow { +/// // custom traversal logic +/// ControlFlow::Continue(()) +/// } +/// +/// #[derive(TraversableMut)] +/// struct User { +/// #[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;