toml_edit/
visit.rs

1#![allow(missing_docs)]
2
3//! Document tree traversal to walk a shared borrow of a document tree.
4//!
5//! Each method of the [`Visit`] trait is a hook that can be overridden
6//! to customize the behavior when mutating the corresponding type of node.
7//! By default, every method recursively visits the substructure of the
8//! input by invoking the right visitor method of each of its fields.
9//!
10//! ```
11//! # use toml_edit::{Item, ArrayOfTables, Table, Value};
12//!
13//! pub trait Visit<'doc> {
14//!     /* ... */
15//!
16//!     fn visit_item(&mut self, i: &'doc Item) {
17//!         visit_item(self, i);
18//!     }
19//!
20//!     /* ... */
21//!     # fn visit_value(&mut self, i: &'doc Value);
22//!     # fn visit_table(&mut self, i: &'doc Table);
23//!     # fn visit_array_of_tables(&mut self, i: &'doc ArrayOfTables);
24//! }
25//!
26//! pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item)
27//! where
28//!     V: Visit<'doc> + ?Sized,
29//! {
30//!     match node {
31//!         Item::None => {}
32//!         Item::Value(value) => v.visit_value(value),
33//!         Item::Table(table) => v.visit_table(table),
34//!         Item::ArrayOfTables(array) => v.visit_array_of_tables(array),
35//!     }
36//! }
37//! ```
38//!
39//! The API is modeled after [`syn::visit`](https://docs.rs/syn/1/syn/visit).
40//!
41//! # Examples
42//!
43//! This visitor stores every string in the document.
44//!
45//! ```
46//! # #[cfg(feature = "parse")] {
47//! # use toml_edit::*;
48//! use toml_edit::visit::*;
49//!
50//! #[derive(Default)]
51//! struct StringCollector<'doc> {
52//!     strings: Vec<&'doc str>,
53//! }
54//!
55//! impl<'doc> Visit<'doc> for StringCollector<'doc> {
56//!     fn visit_string(&mut self, node: &'doc Formatted<String>) {
57//!          self.strings.push(node.value().as_str());
58//!     }
59//! }
60//!
61//! let input = r#"
62//! laputa = "sky-castle"
63//! the-force = { value = "surrounds-you" }
64//! "#;
65//!
66//! let mut document: DocumentMut = input.parse().unwrap();
67//! let mut visitor = StringCollector::default();
68//! visitor.visit_document(&document);
69//!
70//! assert_eq!(visitor.strings, vec!["sky-castle", "surrounds-you"]);
71//! # }
72//! ```
73//!
74//! For a more complex example where the visitor has internal state, see `examples/visit.rs`
75//! [on GitHub](https://github.com/toml-rs/toml/blob/main/crates/toml_edit/examples/visit.rs).
76
77use crate::{
78    Array, ArrayOfTables, Datetime, DocumentMut, Formatted, InlineTable, Item, Table, TableLike,
79    Value,
80};
81
82/// Document tree traversal to mutate an exclusive borrow of a document tree in-place.
83///
84/// See the [module documentation](self) for details.
85pub trait Visit<'doc> {
86    fn visit_document(&mut self, node: &'doc DocumentMut) {
87        visit_document(self, node);
88    }
89
90    fn visit_item(&mut self, node: &'doc Item) {
91        visit_item(self, node);
92    }
93
94    fn visit_table(&mut self, node: &'doc Table) {
95        visit_table(self, node);
96    }
97
98    fn visit_inline_table(&mut self, node: &'doc InlineTable) {
99        visit_inline_table(self, node);
100    }
101
102    fn visit_table_like(&mut self, node: &'doc dyn TableLike) {
103        visit_table_like(self, node);
104    }
105
106    fn visit_table_like_kv(&mut self, key: &'doc str, node: &'doc Item) {
107        visit_table_like_kv(self, key, node);
108    }
109
110    fn visit_array(&mut self, node: &'doc Array) {
111        visit_array(self, node);
112    }
113
114    fn visit_array_of_tables(&mut self, node: &'doc ArrayOfTables) {
115        visit_array_of_tables(self, node);
116    }
117
118    fn visit_value(&mut self, node: &'doc Value) {
119        visit_value(self, node);
120    }
121
122    fn visit_boolean(&mut self, node: &'doc Formatted<bool>) {
123        visit_boolean(self, node);
124    }
125
126    fn visit_datetime(&mut self, node: &'doc Formatted<Datetime>) {
127        visit_datetime(self, node);
128    }
129
130    fn visit_float(&mut self, node: &'doc Formatted<f64>) {
131        visit_float(self, node);
132    }
133
134    fn visit_integer(&mut self, node: &'doc Formatted<i64>) {
135        visit_integer(self, node);
136    }
137
138    fn visit_string(&mut self, node: &'doc Formatted<String>) {
139        visit_string(self, node);
140    }
141}
142
143pub fn visit_document<'doc, V>(v: &mut V, node: &'doc DocumentMut)
144where
145    V: Visit<'doc> + ?Sized,
146{
147    v.visit_table(node.as_table());
148}
149
150pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item)
151where
152    V: Visit<'doc> + ?Sized,
153{
154    match node {
155        Item::None => {}
156        Item::Value(value) => v.visit_value(value),
157        Item::Table(table) => v.visit_table(table),
158        Item::ArrayOfTables(array) => v.visit_array_of_tables(array),
159    }
160}
161
162pub fn visit_table<'doc, V>(v: &mut V, node: &'doc Table)
163where
164    V: Visit<'doc> + ?Sized,
165{
166    v.visit_table_like(node);
167}
168
169pub fn visit_inline_table<'doc, V>(v: &mut V, node: &'doc InlineTable)
170where
171    V: Visit<'doc> + ?Sized,
172{
173    v.visit_table_like(node);
174}
175
176pub fn visit_table_like<'doc, V>(v: &mut V, node: &'doc dyn TableLike)
177where
178    V: Visit<'doc> + ?Sized,
179{
180    for (key, item) in node.iter() {
181        v.visit_table_like_kv(key, item);
182    }
183}
184
185pub fn visit_table_like_kv<'doc, V>(v: &mut V, _key: &'doc str, node: &'doc Item)
186where
187    V: Visit<'doc> + ?Sized,
188{
189    v.visit_item(node);
190}
191
192pub fn visit_array<'doc, V>(v: &mut V, node: &'doc Array)
193where
194    V: Visit<'doc> + ?Sized,
195{
196    for value in node.iter() {
197        v.visit_value(value);
198    }
199}
200
201pub fn visit_array_of_tables<'doc, V>(v: &mut V, node: &'doc ArrayOfTables)
202where
203    V: Visit<'doc> + ?Sized,
204{
205    for table in node.iter() {
206        v.visit_table(table);
207    }
208}
209
210pub fn visit_value<'doc, V>(v: &mut V, node: &'doc Value)
211where
212    V: Visit<'doc> + ?Sized,
213{
214    match node {
215        Value::String(s) => v.visit_string(s),
216        Value::Integer(i) => v.visit_integer(i),
217        Value::Float(f) => v.visit_float(f),
218        Value::Boolean(b) => v.visit_boolean(b),
219        Value::Datetime(dt) => v.visit_datetime(dt),
220        Value::Array(array) => v.visit_array(array),
221        Value::InlineTable(table) => v.visit_inline_table(table),
222    }
223}
224
225macro_rules! empty_visit {
226    ($name: ident, $t: ty) => {
227        fn $name<'doc, V>(_v: &mut V, _node: &'doc $t)
228        where
229            V: Visit<'doc> + ?Sized,
230        {
231        }
232    };
233}
234
235empty_visit!(visit_boolean, Formatted<bool>);
236empty_visit!(visit_datetime, Formatted<Datetime>);
237empty_visit!(visit_float, Formatted<f64>);
238empty_visit!(visit_integer, Formatted<i64>);
239empty_visit!(visit_string, Formatted<String>);