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//! # use toml_edit::*;
47//! use toml_edit::visit::*;
48//!
49//! #[derive(Default)]
50//! struct StringCollector<'doc> {
51//!     strings: Vec<&'doc str>,
52//! }
53//!
54//! impl<'doc> Visit<'doc> for StringCollector<'doc> {
55//!     fn visit_string(&mut self, node: &'doc Formatted<String>) {
56//!          self.strings.push(node.value().as_str());
57//!     }
58//! }
59//!
60//! let input = r#"
61//! laputa = "sky-castle"
62//! the-force = { value = "surrounds-you" }
63//! "#;
64//!
65//! let mut document: Document = input.parse().unwrap();
66//! let mut visitor = StringCollector::default();
67//! visitor.visit_document(&document);
68//!
69//! assert_eq!(visitor.strings, vec!["sky-castle", "surrounds-you"]);
70//! ```
71//!
72//! For a more complex example where the visitor has internal state, see `examples/visit.rs`
73//! [on GitHub](https://github.com/ordian/toml_edit/blob/master/examples/visit.rs).
74
75use crate::{
76    Array, ArrayOfTables, Datetime, Document, Formatted, InlineTable, Item, Table, TableLike, Value,
77};
78
79/// Document tree traversal to mutate an exclusive borrow of a document tree in-place.
80///
81/// See the [module documentation](self) for details.
82pub trait Visit<'doc> {
83    fn visit_document(&mut self, node: &'doc Document) {
84        visit_document(self, node);
85    }
86
87    fn visit_item(&mut self, node: &'doc Item) {
88        visit_item(self, node);
89    }
90
91    fn visit_table(&mut self, node: &'doc Table) {
92        visit_table(self, node);
93    }
94
95    fn visit_inline_table(&mut self, node: &'doc InlineTable) {
96        visit_inline_table(self, node)
97    }
98
99    fn visit_table_like(&mut self, node: &'doc dyn TableLike) {
100        visit_table_like(self, node);
101    }
102
103    fn visit_table_like_kv(&mut self, key: &'doc str, node: &'doc Item) {
104        visit_table_like_kv(self, key, node);
105    }
106
107    fn visit_array(&mut self, node: &'doc Array) {
108        visit_array(self, node);
109    }
110
111    fn visit_array_of_tables(&mut self, node: &'doc ArrayOfTables) {
112        visit_array_of_tables(self, node);
113    }
114
115    fn visit_value(&mut self, node: &'doc Value) {
116        visit_value(self, node);
117    }
118
119    fn visit_boolean(&mut self, node: &'doc Formatted<bool>) {
120        visit_boolean(self, node)
121    }
122
123    fn visit_datetime(&mut self, node: &'doc Formatted<Datetime>) {
124        visit_datetime(self, node);
125    }
126
127    fn visit_float(&mut self, node: &'doc Formatted<f64>) {
128        visit_float(self, node)
129    }
130
131    fn visit_integer(&mut self, node: &'doc Formatted<i64>) {
132        visit_integer(self, node)
133    }
134
135    fn visit_string(&mut self, node: &'doc Formatted<String>) {
136        visit_string(self, node)
137    }
138}
139
140pub fn visit_document<'doc, V>(v: &mut V, node: &'doc Document)
141where
142    V: Visit<'doc> + ?Sized,
143{
144    v.visit_table(node.as_table());
145}
146
147pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item)
148where
149    V: Visit<'doc> + ?Sized,
150{
151    match node {
152        Item::None => {}
153        Item::Value(value) => v.visit_value(value),
154        Item::Table(table) => v.visit_table(table),
155        Item::ArrayOfTables(array) => v.visit_array_of_tables(array),
156    }
157}
158
159pub fn visit_table<'doc, V>(v: &mut V, node: &'doc Table)
160where
161    V: Visit<'doc> + ?Sized,
162{
163    v.visit_table_like(node)
164}
165
166pub fn visit_inline_table<'doc, V>(v: &mut V, node: &'doc InlineTable)
167where
168    V: Visit<'doc> + ?Sized,
169{
170    v.visit_table_like(node)
171}
172
173pub fn visit_table_like<'doc, V>(v: &mut V, node: &'doc dyn TableLike)
174where
175    V: Visit<'doc> + ?Sized,
176{
177    for (key, item) in node.iter() {
178        v.visit_table_like_kv(key, item)
179    }
180}
181
182pub fn visit_table_like_kv<'doc, V>(v: &mut V, _key: &'doc str, node: &'doc Item)
183where
184    V: Visit<'doc> + ?Sized,
185{
186    v.visit_item(node)
187}
188
189pub fn visit_array<'doc, V>(v: &mut V, node: &'doc Array)
190where
191    V: Visit<'doc> + ?Sized,
192{
193    for value in node.iter() {
194        v.visit_value(value);
195    }
196}
197
198pub fn visit_array_of_tables<'doc, V>(v: &mut V, node: &'doc ArrayOfTables)
199where
200    V: Visit<'doc> + ?Sized,
201{
202    for table in node.iter() {
203        v.visit_table(table);
204    }
205}
206
207pub fn visit_value<'doc, V>(v: &mut V, node: &'doc Value)
208where
209    V: Visit<'doc> + ?Sized,
210{
211    match node {
212        Value::String(s) => v.visit_string(s),
213        Value::Integer(i) => v.visit_integer(i),
214        Value::Float(f) => v.visit_float(f),
215        Value::Boolean(b) => v.visit_boolean(b),
216        Value::Datetime(dt) => v.visit_datetime(dt),
217        Value::Array(array) => v.visit_array(array),
218        Value::InlineTable(table) => v.visit_inline_table(table),
219    }
220}
221
222macro_rules! empty_visit {
223    ($name: ident, $t: ty) => {
224        fn $name<'doc, V>(_v: &mut V, _node: &'doc $t)
225        where
226            V: Visit<'doc> + ?Sized,
227        {
228        }
229    };
230}
231
232empty_visit!(visit_boolean, Formatted<bool>);
233empty_visit!(visit_datetime, Formatted<Datetime>);
234empty_visit!(visit_float, Formatted<f64>);
235empty_visit!(visit_integer, Formatted<i64>);
236empty_visit!(visit_string, Formatted<String>);