Skip to main content

toml_spanner/
lib.rs

1//! A high-performance TOML parser that preserves span information for
2//! values and keys.
3//!
4//! # Parsing and Traversal
5//!
6//! Use [`parse`] with a TOML string and an [`Arena`] to get a [`Root`].
7//! ```
8//! # fn main() -> Result<(), toml_spanner::Error> {
9//! let arena = toml_spanner::Arena::new();
10//! let root = toml_spanner::parse("key = 'value'", &arena)?;
11//! # Ok(())
12//! # }
13//! ```
14//! Traverse the tree for inspection via index operators which return a [`MaybeItem`]:
15//! ```
16//! # let arena = toml_spanner::Arena::new();
17//! # let root = toml_spanner::parse("", &arena).unwrap();
18//! let name: Option<&str> = root["name"].as_str();
19//! let numbers: Option<i64> = root["numbers"][50].as_i64();
20//! ```
21//! Use the [`MaybeItem::item()`] method get an [`Item`] which contains a [`Value`] and [`Span`].
22//! ```rust
23//! # use toml_spanner::{Value, Span};
24//! # let arena = toml_spanner::Arena::new();
25//! # let root = toml_spanner::parse("item = 0", &arena).unwrap();
26//! let Some(item) = root["item"].item() else {
27//!     panic!("Missing key `custom`");
28//! };
29//! match item.value() {
30//!      Value::String(string) => {},
31//!      Value::Integer(integer) => {}
32//!      Value::Float(float) => {},
33//!      Value::Boolean(boolean) => {},
34//!      Value::Array(array) => {},
35//!      Value::Table(table) => {},
36//!      Value::DateTime(date_time) => {},
37//! }
38//! // Get byte offset of where item was defined in the source.
39//! let Span{start, end} = item.span();
40//! ```
41//!
42//! ## Deserialization
43//!
44//! Use [`Root::helper()`] to create a [`TableHelper`] for type-safe field extraction
45//! via the [`Deserialize`] trait. Errors are accumulated in the [`Root`]'s context
46//! rather than failing on the first error.
47//!
48//! ```
49//! # fn main() -> Result<(), toml_spanner::Error> {
50//! # let arena = toml_spanner::Arena::new();
51//! # let mut root = toml_spanner::parse("name = 'hello'", &arena)?;
52//! let mut helper = root.helper();
53//! let name: String = helper.required("name").ok().unwrap();
54//! # Ok(())
55//! # }
56//! ```
57//!
58//! Extract values with [`Item::parse`] which uses [`std::str::FromStr`] expecting a String kinded TOML Value.
59//!
60//! ```
61//! # fn main() -> Result<(), toml_spanner::Error> {
62//! # let arena = toml_spanner::Arena::new();
63//! # let root = toml_spanner::parse("ip-address = '127.0.0.1'", &arena)?;
64//! let item = root["ip-address"].item().unwrap();
65//! let ip: std::net::Ipv4Addr = item.parse()?;
66//! # Ok(())
67//! # }
68//! ```
69//!
70//! <details>
71//! <summary>Toggle More Extensive Example</summary>
72//!
73//! ```
74//! use toml_spanner::{Arena, Deserialize, Item, Context, Failed, TableHelper};
75//!
76//! #[derive(Debug)]
77//! struct Things {
78//!     name: String,
79//!     value: u32,
80//!     color: Option<String>,
81//! }
82//!
83//! impl<'de> Deserialize<'de> for Things {
84//!     fn deserialize(ctx: &mut Context<'de>, value: &Item<'de>) -> Result<Self, Failed> {
85//!         let Some(table) = value.as_table() else {
86//!             return Err(ctx.error_expected_but_found("a table", value));
87//!         };
88//!         let mut th = TableHelper::new(ctx, table);
89//!         let name = th.required("name")?;
90//!         let value = th.required("value")?;
91//!         let color = th.optional("color");
92//!         th.expect_empty()?;
93//!         Ok(Things { name, value, color })
94//!     }
95//! }
96//!
97//! let content = r#"
98//! dev-mode = true
99//!
100//! [[things]]
101//! name = "hammer"
102//! value = 43
103//!
104//! [[things]]
105//! name = "drill"
106//! value = 300
107//! color = "green"
108//! "#;
109//!
110//! let arena = Arena::new();
111//! let mut root = toml_spanner::parse(content, &arena)?;
112//!
113//! // Null-coalescing index operators — missing keys return a None-like
114//! // MaybeItem instead of panicking.
115//! assert_eq!(root["things"][0]["color"].as_str(), None);
116//! assert_eq!(root["things"][1]["color"].as_str(), Some("green"));
117//!
118//! // Deserialize typed values out of the root table.
119//! let mut helper = root.helper();
120//! let things: Vec<Things> = helper.required("things").ok().unwrap();
121//! let dev_mode: bool = helper.optional("dev-mode").unwrap_or(false);
122//! // Error if unconsumed fields remain.
123//! helper.expect_empty().ok();
124//!
125//! assert_eq!(things.len(), 2);
126//! assert_eq!(things[0].name, "hammer");
127//! assert!(dev_mode);
128//! # Ok::<(), toml_spanner::Error>(())
129//! ```
130//!
131//! </details>
132
133mod arena;
134mod array;
135#[cfg(feature = "deserialization")]
136mod de;
137mod error;
138mod parser;
139mod span;
140mod table;
141mod time;
142mod value;
143
144pub use arena::Arena;
145pub use array::Array;
146#[cfg(feature = "deserialization")]
147pub use de::{Context, Deserialize, Failed, TableHelper};
148pub use error::{Error, ErrorKind};
149pub use parser::{Root, parse};
150pub use span::{Span, Spanned};
151pub use table::Table;
152pub use time::{Date, DateTime, Time, TimeOffset};
153pub use value::{Item, Key, Kind, MaybeItem, Value, ValueMut};
154
155#[cfg(feature = "serde")]
156pub mod impl_serde;