Skip to content

Commit 7bd3090

Browse files
authored
refactor: visitor implements enter leave (#4)
Signed-off-by: tison <wander4096@gmail.com>
1 parent 7027d1b commit 7bd3090

File tree

4 files changed

+119
-113
lines changed

4 files changed

+119
-113
lines changed

visitor-derive/src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,24 +216,29 @@ fn impl_traversable(input: DeriveInput, mutable: bool) -> Result<TokenStream> {
216216
Span::call_site(),
217217
);
218218

219-
let visit_method = Ident::new(
220-
if mutable { "visit_mut" } else { "visit" },
219+
let enter_method = Ident::new(
220+
if mutable { "enter_mut" } else { "enter" },
221+
Span::call_site(),
222+
);
223+
224+
let leave_method = Ident::new(
225+
if mutable { "leave_mut" } else { "leave" },
221226
Span::call_site(),
222227
);
223228

224229
let enter_self = if skip_visit_self {
225230
None
226231
} else {
227232
Some(quote! {
228-
::visitor::#visitor::#visit_method(visitor, self, ::visitor::Event::Enter);
233+
::visitor::#visitor::#enter_method(visitor, self);
229234
})
230235
};
231236

232-
let exit_self = if skip_visit_self {
237+
let leave_self = if skip_visit_self {
233238
None
234239
} else {
235240
Some(quote! {
236-
::visitor::#visitor::#visit_method(visitor, self, ::visitor::Event::Exit);
241+
::visitor::#visitor::#leave_method(visitor, self);
237242
})
238243
};
239244

@@ -273,7 +278,7 @@ fn impl_traversable(input: DeriveInput, mutable: bool) -> Result<TokenStream> {
273278
fn #method<V: ::visitor::#visitor>(& #mut_modifier self, visitor: &mut V) {
274279
#enter_self
275280
#traverse_fields
276-
#exit_self
281+
#leave_self
277282
}
278283
}
279284
})

visitor/src/function/mod.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2025 FastLabs Developers
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
//! Visitors from functions or closures.
16+
17+
use core::any::Any;
18+
use core::marker::PhantomData;
19+
20+
use crate::Visitor;
21+
use crate::VisitorMut;
22+
23+
/// Type returned by `make_visitor` factories.
24+
pub struct FnVisitor<T, F1, F2> {
25+
enter: F1,
26+
leave: F2,
27+
m: PhantomData<T>,
28+
}
29+
30+
impl<T: Any, F1: FnMut(&T), F2: FnMut(&T)> Visitor for FnVisitor<T, F1, F2> {
31+
fn enter(&mut self, this: &dyn Any) {
32+
if let Some(item) = this.downcast_ref::<T>() {
33+
(self.enter)(item);
34+
}
35+
}
36+
37+
fn leave(&mut self, this: &dyn Any) {
38+
if let Some(item) = this.downcast_ref::<T>() {
39+
(self.leave)(item);
40+
}
41+
}
42+
}
43+
44+
impl<T: Any, F1: FnMut(&mut T), F2: FnMut(&mut T)> VisitorMut for FnVisitor<T, F1, F2> {
45+
fn enter_mut(&mut self, this: &mut dyn Any) {
46+
if let Some(item) = this.downcast_mut::<T>() {
47+
(self.enter)(item);
48+
}
49+
}
50+
51+
fn leave_mut(&mut self, this: &mut dyn Any) {
52+
if let Some(item) = this.downcast_mut::<T>() {
53+
(self.leave)(item);
54+
}
55+
}
56+
}
57+
58+
/// Create a visitor that only visits items of a specific type from a function or a closure.
59+
pub fn make_visitor<T, F1: FnMut(&T), F2: FnMut(&T)>(enter: F1, leave: F2) -> FnVisitor<T, F1, F2> {
60+
FnVisitor {
61+
enter,
62+
leave,
63+
m: PhantomData,
64+
}
65+
}
66+
67+
/// Create a visitor that only visits mutable items of a specific type from a function or a closure.
68+
pub fn make_visitor_mut<T, F1: FnMut(&mut T), F2: FnMut(&mut T)>(
69+
enter: F1,
70+
leave: F2,
71+
) -> FnVisitor<T, F1, F2> {
72+
FnVisitor {
73+
enter,
74+
leave,
75+
m: PhantomData,
76+
}
77+
}

visitor/src/lib.rs

Lines changed: 25 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -28,107 +28,34 @@ pub use visitor_derive::Traversable;
2828
/// See [`TraversableMut`].
2929
pub use visitor_derive::TraversableMut;
3030

31-
pub use self::impl_visitor::*;
32-
33-
/// Whether the visitor is entering or exiting a node.
34-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35-
pub enum Event {
36-
/// The visitor is entering a node.
37-
Enter,
38-
/// The visitor is exiting a node.
39-
Exit,
40-
}
31+
pub mod function;
4132

4233
/// A visitor that can be used to traverse a data structure.
4334
pub trait Visitor {
44-
/// Called when the visitor is traversing a node.
45-
fn visit(&mut self, this: &dyn core::any::Any, event: Event);
35+
/// Called when the visitor is entering a node.
36+
///
37+
/// Default implementation does nothing.
38+
#[expect(unused_variables)]
39+
fn enter(&mut self, this: &dyn core::any::Any) {}
40+
/// Called when the visitor is leaving a node.
41+
///
42+
/// Default implementation does nothing.
43+
#[expect(unused_variables)]
44+
fn leave(&mut self, this: &dyn core::any::Any) {}
4645
}
4746

4847
/// A visitor that can be used to traverse a mutable data structure.
4948
pub trait VisitorMut {
50-
/// Called when the visitor is traversing a mutable node.
51-
fn visit_mut(&mut self, this: &mut dyn core::any::Any, event: Event);
52-
}
53-
54-
mod impl_visitor {
55-
use core::any::Any;
56-
use core::marker::PhantomData;
57-
58-
use super::*;
59-
60-
/// Type returned by [`visitor_fn`].
61-
pub struct FnVisitor<T, F> {
62-
f: F,
63-
m: PhantomData<T>,
64-
}
65-
66-
impl<T: Any, F: FnMut(&T, Event)> Visitor for FnVisitor<T, F> {
67-
fn visit(&mut self, this: &dyn Any, event: Event) {
68-
if let Some(item) = <dyn Any>::downcast_ref::<T>(this) {
69-
(self.f)(item, event);
70-
}
71-
}
72-
}
73-
74-
impl<T: Any, F: FnMut(&mut T, Event)> VisitorMut for FnVisitor<T, F> {
75-
fn visit_mut(&mut self, this: &mut dyn Any, event: Event) {
76-
if let Some(item) = <dyn Any>::downcast_mut::<T>(this) {
77-
(self.f)(item, event);
78-
}
79-
}
80-
}
81-
82-
/// Create a visitor that only visits items of some specific type from a function or a closure.
83-
pub fn visitor_fn<T, F: FnMut(&T, Event)>(f: F) -> FnVisitor<T, F> {
84-
FnVisitor { f, m: PhantomData }
85-
}
86-
87-
/// Similar to [`visitor_fn`], but the closure will only be called on [`Event::Enter`].
88-
pub fn visitor_enter_fn<T, F: FnMut(&T)>(mut f: F) -> FnVisitor<T, impl FnMut(&T, Event)> {
89-
visitor_fn(move |item, event| {
90-
if let Event::Enter = event {
91-
f(item);
92-
}
93-
})
94-
}
95-
96-
/// Similar to [`visitor_fn`], but the closure will only be called on [`Event::Exit`].
97-
pub fn visitor_exit_fn<T, F: FnMut(&T)>(mut f: F) -> FnVisitor<T, impl FnMut(&T, Event)> {
98-
visitor_fn(move |item, event| {
99-
if let Event::Exit = event {
100-
f(item);
101-
}
102-
})
103-
}
104-
105-
/// Create a visitor that only visits mutable items of some specific type from a function or a
106-
/// closure.
107-
pub fn visitor_fn_mut<T, F: FnMut(&mut T, Event)>(f: F) -> FnVisitor<T, F> {
108-
FnVisitor { f, m: PhantomData }
109-
}
110-
111-
/// Similar to [`visitor_fn_mut`], but the closure will only be called on [`Event::Enter`].
112-
pub fn visitor_enter_fn_mut<T, F: FnMut(&mut T)>(
113-
mut f: F,
114-
) -> FnVisitor<T, impl FnMut(&mut T, Event)> {
115-
visitor_fn_mut(move |item, event| {
116-
if let Event::Enter = event {
117-
f(item);
118-
}
119-
})
120-
}
121-
122-
/// Similar to [`visitor_fn_mut`], but the closure will only be called on [`Event::Exit`].
123-
pub fn visitor_exit_fn_mut<T, F: FnMut(&mut T)>(
124-
mut f: F,
125-
) -> FnVisitor<T, impl FnMut(&mut T, Event)> {
126-
visitor_fn_mut(move |item, event| {
127-
if let Event::Exit = event {
128-
f(item);
129-
}
130-
})
131-
}
49+
/// Called when the visitor is entering a mutable node.
50+
///
51+
/// Default implementation does nothing.
52+
#[expect(unused_variables)]
53+
fn enter_mut(&mut self, this: &mut dyn core::any::Any) {}
54+
/// Called when the visitor is leaving a mutable node.
55+
///
56+
/// Default implementation does nothing.
57+
#[expect(unused_variables)]
58+
fn leave_mut(&mut self, this: &mut dyn core::any::Any) {}
13259
}
13360

13461
/// A trait for types that can be traversed by a visitor.
@@ -161,15 +88,15 @@ macro_rules! trivial_traverse_impl {
16188
( $type:ty ) => {
16289
impl Traversable for $type {
16390
fn traverse<V: Visitor>(&self, visitor: &mut V) {
164-
visitor.visit(self, Event::Enter);
165-
visitor.visit(self, Event::Exit);
91+
visitor.enter(self);
92+
visitor.leave(self);
16693
}
16794
}
16895

16996
impl TraversableMut for $type {
17097
fn traverse_mut<V: VisitorMut>(&mut self, visitor: &mut V) {
171-
visitor.visit_mut(self, Event::Enter);
172-
visitor.visit_mut(self, Event::Exit);
98+
visitor.enter_mut(self);
99+
visitor.leave_mut(self);
173100
}
174101
}
175102
};

visitor/tests/test_visitor_mut.rs

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
use std::any::Any;
1818

19-
use visitor::Event;
2019
use visitor::TraversableMut;
2120
use visitor::VisitorMut;
2221

@@ -40,14 +39,12 @@ struct ChainCutter {
4039
}
4140

4241
impl VisitorMut for ChainCutter {
43-
fn visit_mut(&mut self, this: &mut dyn Any, event: Event) {
44-
if matches!(event, Event::Enter) {
45-
if let Some(item) = this.downcast_mut::<Chain>() {
46-
if self.cut_at_depth == 0 {
47-
item.next = None;
48-
} else {
49-
self.cut_at_depth -= 1;
50-
}
42+
fn enter_mut(&mut self, this: &mut dyn Any) {
43+
if let Some(item) = this.downcast_mut::<Chain>() {
44+
if self.cut_at_depth == 0 {
45+
item.next = None;
46+
} else {
47+
self.cut_at_depth -= 1;
5148
}
5249
}
5350
}

0 commit comments

Comments
 (0)