diff options
Diffstat (limited to 'vendor/toml_edit/examples/visit.rs')
-rw-r--r-- | vendor/toml_edit/examples/visit.rs | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/vendor/toml_edit/examples/visit.rs b/vendor/toml_edit/examples/visit.rs new file mode 100644 index 0000000..cd7f851 --- /dev/null +++ b/vendor/toml_edit/examples/visit.rs @@ -0,0 +1,284 @@ +//! Example for how to use `VisitMut` to iterate over a table. + +use std::collections::BTreeSet; +use toml_edit::visit::*; +use toml_edit::visit_mut::*; +use toml_edit::{Array, Document, InlineTable, Item, KeyMut, Table, Value}; + +/// This models the visit state for dependency keys in a `Cargo.toml`. +/// +/// Dependencies can be specified as: +/// +/// ```toml +/// [dependencies] +/// dep1 = "0.2" +/// +/// [build-dependencies] +/// dep2 = "0.3" +/// +/// [dev-dependencies] +/// dep3 = "0.4" +/// +/// [target.'cfg(windows)'.dependencies] +/// dep4 = "0.5" +/// +/// # and target build- and dev-dependencies +/// ``` +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +enum VisitState { + /// Represents the root of the table. + Root, + /// Represents "dependencies", "build-dependencies" or "dev-dependencies", or the target + /// forms of these. + Dependencies, + /// A table within dependencies. + SubDependencies, + /// Represents "target". + Target, + /// "target.[TARGET]". + TargetWithSpec, + /// Represents some other state. + Other, +} + +impl VisitState { + /// Figures out the next visit state, given the current state and the given key. + fn descend(self, key: &str) -> Self { + match (self, key) { + ( + VisitState::Root | VisitState::TargetWithSpec, + "dependencies" | "build-dependencies" | "dev-dependencies", + ) => VisitState::Dependencies, + (VisitState::Root, "target") => VisitState::Target, + (VisitState::Root | VisitState::TargetWithSpec, _) => VisitState::Other, + (VisitState::Target, _) => VisitState::TargetWithSpec, + (VisitState::Dependencies, _) => VisitState::SubDependencies, + (VisitState::SubDependencies, _) => VisitState::SubDependencies, + (VisitState::Other, _) => VisitState::Other, + } + } +} + +/// Collect the names of every dependency key. +#[derive(Debug)] +struct DependencyNameVisitor<'doc> { + state: VisitState, + names: BTreeSet<&'doc str>, +} + +impl<'doc> Visit<'doc> for DependencyNameVisitor<'doc> { + fn visit_table_like_kv(&mut self, key: &'doc str, node: &'doc Item) { + if self.state == VisitState::Dependencies { + self.names.insert(key); + } else { + // Since we're only interested in collecting the top-level keys right under + // [dependencies], don't recurse unconditionally. + + let old_state = self.state; + + // Figure out the next state given the key. + self.state = self.state.descend(key); + + // Recurse further into the document tree. + visit_table_like_kv(self, key, node); + + // Restore the old state after it's done. + self.state = old_state; + } + } +} + +/// Normalize all dependency tables into the format: +/// +/// ```toml +/// [dependencies] +/// dep = { version = "1.0", features = ["foo", "bar"], ... } +/// ``` +/// +/// leaving other tables untouched. +#[derive(Debug)] +struct NormalizeDependencyTablesVisitor { + state: VisitState, +} + +impl VisitMut for NormalizeDependencyTablesVisitor { + fn visit_table_mut(&mut self, node: &mut Table) { + visit_table_mut(self, node); + + // The conversion from regular tables into inline ones might leave some explicit parent + // tables hanging, so convert them to implicit. + if matches!(self.state, VisitState::Target | VisitState::TargetWithSpec) { + node.set_implicit(true); + } + } + + fn visit_table_like_kv_mut(&mut self, mut key: KeyMut<'_>, node: &mut Item) { + let old_state = self.state; + + // Figure out the next state given the key. + self.state = self.state.descend(key.get()); + + match self.state { + VisitState::Target | VisitState::TargetWithSpec | VisitState::Dependencies => { + // Top-level dependency row, or above: turn inline tables into regular ones. + if let Item::Value(Value::InlineTable(inline_table)) = node { + let inline_table = std::mem::replace(inline_table, InlineTable::new()); + let table = inline_table.into_table(); + key.fmt(); + *node = Item::Table(table); + } + } + VisitState::SubDependencies => { + // Individual dependency: turn regular tables into inline ones. + if let Item::Table(table) = node { + // Turn the table into an inline table. + let table = std::mem::replace(table, Table::new()); + let inline_table = table.into_inline_table(); + key.fmt(); + *node = Item::Value(Value::InlineTable(inline_table)); + } + } + _ => {} + } + + // Recurse further into the document tree. + visit_table_like_kv_mut(self, key, node); + + // Restore the old state after it's done. + self.state = old_state; + } + + fn visit_array_mut(&mut self, node: &mut Array) { + // Format any arrays within dependencies to be on the same line. + if matches!( + self.state, + VisitState::Dependencies | VisitState::SubDependencies + ) { + node.fmt(); + } + } +} + +/// This is the input provided to visit_mut_example. +static INPUT: &str = r#" +[package] +name = "my-package" + +[package.metadata.foo] +bar = 42 + +[dependencies] +atty = "0.2" +cargo-platform = { path = "crates/cargo-platform", version = "0.1.2" } + +[dependencies.pretty_env_logger] +version = "0.4" +optional = true + +[target.'cfg(windows)'.dependencies] +fwdansi = "1.1.0" + +[target.'cfg(windows)'.dependencies.winapi] +version = "0.3" +features = [ +"handleapi", +"jobapi", +] + +[target.'cfg(unix)'] +dev-dependencies = { miniz_oxide = "0.5" } + +[dev-dependencies.cargo-test-macro] +path = "crates/cargo-test-macro" + +[build-dependencies.flate2] +version = "0.4" +"#; + +/// This is the output produced by visit_mut_example. +#[cfg(test)] +static VISIT_MUT_OUTPUT: &str = r#" +[package] +name = "my-package" + +[package.metadata.foo] +bar = 42 + +[dependencies] +atty = "0.2" +cargo-platform = { path = "crates/cargo-platform", version = "0.1.2" } +pretty_env_logger = { version = "0.4", optional = true } + +[target.'cfg(windows)'.dependencies] +fwdansi = "1.1.0" +winapi = { version = "0.3", features = ["handleapi", "jobapi"] } + +[target.'cfg(unix)'.dev-dependencies] +miniz_oxide = "0.5" + +[dev-dependencies] +cargo-test-macro = { path = "crates/cargo-test-macro" } + +[build-dependencies] +flate2 = { version = "0.4" } +"#; + +fn visit_example(document: &Document) -> BTreeSet<&str> { + let mut visitor = DependencyNameVisitor { + state: VisitState::Root, + names: BTreeSet::new(), + }; + + visitor.visit_document(document); + + visitor.names +} + +fn visit_mut_example(document: &mut Document) { + let mut visitor = NormalizeDependencyTablesVisitor { + state: VisitState::Root, + }; + + visitor.visit_document_mut(document); +} + +fn main() { + let mut document: Document = INPUT.parse().expect("input is valid TOML"); + + println!("** visit example"); + println!("{:?}", visit_example(&document)); + + println!("** visit_mut example"); + visit_mut_example(&mut document); + println!("{}", document); +} + +#[cfg(test)] +#[test] +fn visit_correct() { + let document: Document = INPUT.parse().expect("input is valid TOML"); + + let names = visit_example(&document); + let expected = vec![ + "atty", + "cargo-platform", + "pretty_env_logger", + "fwdansi", + "winapi", + "miniz_oxide", + "cargo-test-macro", + "flate2", + ] + .into_iter() + .collect(); + assert_eq!(names, expected); +} + +#[cfg(test)] +#[test] +fn visit_mut_correct() { + let mut document: Document = INPUT.parse().expect("input is valid TOML"); + + visit_mut_example(&mut document); + assert_eq!(format!("{}", document), VISIT_MUT_OUTPUT); +} |