diff options
Diffstat (limited to 'src/visit.rs')
-rw-r--r-- | src/visit.rs | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/src/visit.rs b/src/visit.rs new file mode 100644 index 0000000..1bc640a --- /dev/null +++ b/src/visit.rs @@ -0,0 +1,236 @@ +#![allow(missing_docs)] + +//! Document tree traversal to walk a shared borrow of a document tree. +//! +//! Each method of the [`Visit`] trait is a hook that can be overridden +//! to customize the behavior when mutating the corresponding type of node. +//! By default, every method recursively visits the substructure of the +//! input by invoking the right visitor method of each of its fields. +//! +//! ``` +//! # use toml_edit::{Item, ArrayOfTables, Table, Value}; +//! +//! pub trait Visit<'doc> { +//! /* ... */ +//! +//! fn visit_item(&mut self, i: &'doc Item) { +//! visit_item(self, i); +//! } +//! +//! /* ... */ +//! # fn visit_value(&mut self, i: &'doc Value); +//! # fn visit_table(&mut self, i: &'doc Table); +//! # fn visit_array_of_tables(&mut self, i: &'doc ArrayOfTables); +//! } +//! +//! pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item) +//! where +//! V: Visit<'doc> + ?Sized, +//! { +//! match node { +//! Item::None => {} +//! Item::Value(value) => v.visit_value(value), +//! Item::Table(table) => v.visit_table(table), +//! Item::ArrayOfTables(array) => v.visit_array_of_tables(array), +//! } +//! } +//! ``` +//! +//! The API is modeled after [`syn::visit`](https://docs.rs/syn/1/syn/visit). +//! +//! # Examples +//! +//! This visitor stores every string in the document. +//! +//! ``` +//! # use toml_edit::*; +//! use toml_edit::visit::*; +//! +//! #[derive(Default)] +//! struct StringCollector<'doc> { +//! strings: Vec<&'doc str>, +//! } +//! +//! impl<'doc> Visit<'doc> for StringCollector<'doc> { +//! fn visit_string(&mut self, node: &'doc Formatted<String>) { +//! self.strings.push(node.value().as_str()); +//! } +//! } +//! +//! let input = r#" +//! laputa = "sky-castle" +//! the-force = { value = "surrounds-you" } +//! "#; +//! +//! let mut document: Document = input.parse().unwrap(); +//! let mut visitor = StringCollector::default(); +//! visitor.visit_document(&document); +//! +//! assert_eq!(visitor.strings, vec!["sky-castle", "surrounds-you"]); +//! ``` +//! +//! For a more complex example where the visitor has internal state, see `examples/visit.rs` +//! [on GitHub](https://github.com/ordian/toml_edit/blob/master/examples/visit.rs). + +use crate::{ + Array, ArrayOfTables, Datetime, Document, Formatted, InlineTable, Item, Table, TableLike, Value, +}; + +/// Document tree traversal to mutate an exclusive borrow of a document tree in-place. +/// +/// See the [module documentation](self) for details. +pub trait Visit<'doc> { + fn visit_document(&mut self, node: &'doc Document) { + visit_document(self, node); + } + + fn visit_item(&mut self, node: &'doc Item) { + visit_item(self, node); + } + + fn visit_table(&mut self, node: &'doc Table) { + visit_table(self, node); + } + + fn visit_inline_table(&mut self, node: &'doc InlineTable) { + visit_inline_table(self, node) + } + + fn visit_table_like(&mut self, node: &'doc dyn TableLike) { + visit_table_like(self, node); + } + + fn visit_table_like_kv(&mut self, key: &'doc str, node: &'doc Item) { + visit_table_like_kv(self, key, node); + } + + fn visit_array(&mut self, node: &'doc Array) { + visit_array(self, node); + } + + fn visit_array_of_tables(&mut self, node: &'doc ArrayOfTables) { + visit_array_of_tables(self, node); + } + + fn visit_value(&mut self, node: &'doc Value) { + visit_value(self, node); + } + + fn visit_boolean(&mut self, node: &'doc Formatted<bool>) { + visit_boolean(self, node) + } + + fn visit_datetime(&mut self, node: &'doc Formatted<Datetime>) { + visit_datetime(self, node); + } + + fn visit_float(&mut self, node: &'doc Formatted<f64>) { + visit_float(self, node) + } + + fn visit_integer(&mut self, node: &'doc Formatted<i64>) { + visit_integer(self, node) + } + + fn visit_string(&mut self, node: &'doc Formatted<String>) { + visit_string(self, node) + } +} + +pub fn visit_document<'doc, V>(v: &mut V, node: &'doc Document) +where + V: Visit<'doc> + ?Sized, +{ + v.visit_table(node.as_table()); +} + +pub fn visit_item<'doc, V>(v: &mut V, node: &'doc Item) +where + V: Visit<'doc> + ?Sized, +{ + match node { + Item::None => {} + Item::Value(value) => v.visit_value(value), + Item::Table(table) => v.visit_table(table), + Item::ArrayOfTables(array) => v.visit_array_of_tables(array), + } +} + +pub fn visit_table<'doc, V>(v: &mut V, node: &'doc Table) +where + V: Visit<'doc> + ?Sized, +{ + v.visit_table_like(node) +} + +pub fn visit_inline_table<'doc, V>(v: &mut V, node: &'doc InlineTable) +where + V: Visit<'doc> + ?Sized, +{ + v.visit_table_like(node) +} + +pub fn visit_table_like<'doc, V>(v: &mut V, node: &'doc dyn TableLike) +where + V: Visit<'doc> + ?Sized, +{ + for (key, item) in node.iter() { + v.visit_table_like_kv(key, item) + } +} + +pub fn visit_table_like_kv<'doc, V>(v: &mut V, _key: &'doc str, node: &'doc Item) +where + V: Visit<'doc> + ?Sized, +{ + v.visit_item(node) +} + +pub fn visit_array<'doc, V>(v: &mut V, node: &'doc Array) +where + V: Visit<'doc> + ?Sized, +{ + for value in node.iter() { + v.visit_value(value); + } +} + +pub fn visit_array_of_tables<'doc, V>(v: &mut V, node: &'doc ArrayOfTables) +where + V: Visit<'doc> + ?Sized, +{ + for table in node.iter() { + v.visit_table(table); + } +} + +pub fn visit_value<'doc, V>(v: &mut V, node: &'doc Value) +where + V: Visit<'doc> + ?Sized, +{ + match node { + Value::String(s) => v.visit_string(s), + Value::Integer(i) => v.visit_integer(i), + Value::Float(f) => v.visit_float(f), + Value::Boolean(b) => v.visit_boolean(b), + Value::Datetime(dt) => v.visit_datetime(dt), + Value::Array(array) => v.visit_array(array), + Value::InlineTable(table) => v.visit_inline_table(table), + } +} + +macro_rules! empty_visit { + ($name: ident, $t: ty) => { + fn $name<'doc, V>(_v: &mut V, _node: &'doc $t) + where + V: Visit<'doc> + ?Sized, + { + } + }; +} + +empty_visit!(visit_boolean, Formatted<bool>); +empty_visit!(visit_datetime, Formatted<Datetime>); +empty_visit!(visit_float, Formatted<f64>); +empty_visit!(visit_integer, Formatted<i64>); +empty_visit!(visit_string, Formatted<String>); |