Expand description

This crate derives 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.

The main building blocks of this crate are two derivable traits:

  • Visitor and VisitorMut implementations walk through data structures and accumulate some information;
  • Drive and DriveMut implementations are data structures that know how to drive a visitor through themselves.

Please refer to these traits’ documentation for more details.

Example

Immutable visitor that counts items in a tree

use derive_visitor::{Visitor, Drive};

#[derive(Drive)]
struct Directory {
    #[drive(skip)]
    name: String,
    items: Vec<DirectoryItem>,
}

#[derive(Drive)]
enum DirectoryItem {
    File(File),
    Directory(Directory),
}

#[derive(Drive)]
struct File {
    #[drive(skip)]
    name: String,
}

#[derive(Visitor, Default)]
#[visitor(File(enter), Directory(enter))]
struct Counter {
    files: u32,
    directories: u32
}

impl Counter {
    fn enter_file(&mut self, _file: &File) {
        self.files += 1;
    }
    fn enter_directory(&mut self, _directory: &Directory) {
        self.directories += 1;
    }
}

let mut counter = Counter::default();

let example_directory = Directory {
    name: "root".into(),
    items: vec![
        DirectoryItem::Directory(
            Directory {
                name: "home".into(),
                items: vec![
                    DirectoryItem::File(File { name: "README.md".into() }),
                    DirectoryItem::File(File { name: "Star Wars.mov".into() })
                ]
            }
        ),
        DirectoryItem::Directory(
            Directory { name: "downloads".into(), items: vec![] }
        )
    ],
};

example_directory.drive(&mut counter);

assert_eq!(counter.files, 2);
assert_eq!(counter.directories, 3);

Mutable visitor that alters a tree

use derive_visitor::{VisitorMut, DriveMut};

#[derive(DriveMut)]
struct Tree {
    #[drive(skip)]
    name: String,
    children: Vec<Tree>
}

#[derive(VisitorMut, Default)]
#[visitor(Tree(enter))]
struct Renamer {
    from: &'static str,
    to: &'static str,
}

impl Renamer {
    fn enter_tree(&mut self, tree: &mut Tree) {
        tree.name = tree.name.replace(self.from, self.to);
    }
}

let mut my_tree = Tree{
    name: "old parent".to_string(),
    children: vec![
        Tree {
            name: "old child".to_string(),
            children: vec![]
        }
    ]
};

my_tree.drive_mut(&mut Renamer{from: "old", to: "new"});

assert_eq!(my_tree.name, "new parent");
assert_eq!(my_tree.children[0].name, "new child");

Features

  • std-types-drive - implement Drive for primitive types and String type from std. It is recommended to either skip these types in your Drive implementation, or to wrap them with newtypes, so this feature is disabled by default. However it might be useful when driving through autogenerated structs.

Structs

Type returned by visitor_fn.

Enums

Defines whether an item is being entered or exited by a visitor.

Traits

A data structure that can drive a visitor through iself.

Drive a VisitorMut over this datastructure.

An interface for visiting arbitrary data structures.

An interface for visiting data structures and mutating them during the visit.

Functions

Similar to visitor_fn, but the closure will only be called on Event::Enter.

Similar to visitor_fn_mut, but the closure will only be called on Event::Enter.

Create a visitor that only visits items of some specific type from a function or a closure.

Create a visitor that only visits items and mutates them with the given function

Derive Macros