Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<File>,
#[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<Self::Break> {
if let Some(file) = node.downcast_ref::<File>() {
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.
Expand Down
4 changes: 2 additions & 2 deletions traversable/src/combinator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
220 changes: 218 additions & 2 deletions traversable/src/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,49 @@ where
type DefaultVisitFn<T, B> = fn(&T) -> ControlFlow<B>;
type DefaultVisitFnMut<T, B> = fn(&mut T) -> ControlFlow<B>;

/// 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::<Item, (), _, _>(
/// |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<T, B, F1, F2>(enter: F1, leave: F2) -> FnVisitor<T, B, F1, F2>
where
T: Any,
Expand All @@ -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::<Item, (), _>(|node: &Item| {
/// // enter closure
/// count += 1;
/// ControlFlow::Continue(())
/// });
///
/// item.traverse(&mut visitor);
/// assert_eq!(count, 1);
/// # }
/// ```
pub fn make_visitor_enter<T, B, F>(enter: F) -> FnVisitor<T, B, F, DefaultVisitFn<T, B>>
where
T: Any,
Expand All @@ -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::<Item, (), _>(|node: &Item| {
/// // leave closure
/// visited_leave = true;
/// ControlFlow::Continue(())
/// });
///
/// item.traverse(&mut visitor);
/// assert!(visited_leave);
/// # }
/// ```
pub fn make_visitor_leave<T, B, F>(leave: F) -> FnVisitor<T, B, DefaultVisitFn<T, B>, F>
where
T: Any,
Expand All @@ -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::<Item, (), _, _>(
/// |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<T, B, F1, F2>(enter: F1, leave: F2) -> FnVisitor<T, B, F1, F2>
where
T: Any,
Expand All @@ -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::<Item, (), _>(|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<T, B, F>(enter: F) -> FnVisitor<T, B, F, DefaultVisitFnMut<T, B>>
where
T: Any,
Expand All @@ -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::<Item, (), _>(|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<T, B, F>(leave: F) -> FnVisitor<T, B, DefaultVisitFnMut<T, B>, F>
where
T: Any,
Expand Down
Loading