facet_toml/lib.rs
1//! TOML serialization for facet using the new format architecture.
2//!
3//! This is the successor to `facet-toml`, using the unified `facet-format` traits.
4//!
5//! # Deserialization
6//!
7//! ```
8//! use facet::Facet;
9//! use facet_toml::from_str;
10//!
11//! #[derive(Facet, Debug)]
12//! struct Config {
13//! name: String,
14//! port: u16,
15//! }
16//!
17//! let toml = r#"
18//! name = "my-app"
19//! port = 8080
20//! "#;
21//!
22//! let config: Config = from_str(toml).unwrap();
23//! assert_eq!(config.name, "my-app");
24//! assert_eq!(config.port, 8080);
25//! ```
26
27extern crate alloc;
28
29mod error;
30mod parser;
31mod serializer;
32
33#[cfg(feature = "axum")]
34mod axum;
35
36pub use error::{TomlError, TomlErrorKind};
37pub use parser::{TomlParser, TomlProbe};
38pub use serializer::{
39 SerializeOptions, TomlSerializeError, TomlSerializer, to_string, to_string_with_options, to_vec,
40};
41
42// Re-export DeserializeError for convenience
43pub use facet_format::DeserializeError;
44
45#[cfg(feature = "axum")]
46pub use axum::{Toml, TomlRejection};
47
48/// Deserialize a value from a TOML string into an owned type.
49///
50/// This is the recommended default for most use cases. The input does not need
51/// to outlive the result, making it suitable for deserializing from temporary
52/// buffers (e.g., HTTP request bodies).
53///
54/// Types containing `&str` fields cannot be deserialized with this function;
55/// use `String` or `Cow<str>` instead. For zero-copy deserialization into
56/// borrowed types, use [`from_str_borrowed`].
57///
58/// # Example
59///
60/// ```
61/// use facet::Facet;
62/// use facet_toml::from_str;
63///
64/// #[derive(Facet, Debug, PartialEq)]
65/// struct Config {
66/// name: String,
67/// port: u16,
68/// }
69///
70/// let toml = r#"
71/// name = "my-app"
72/// port = 8080
73/// "#;
74///
75/// let config: Config = from_str(toml).unwrap();
76/// assert_eq!(config.name, "my-app");
77/// assert_eq!(config.port, 8080);
78/// ```
79pub fn from_str<T>(input: &str) -> Result<T, DeserializeError<TomlError>>
80where
81 T: facet_core::Facet<'static>,
82{
83 use facet_format::FormatDeserializer;
84 let parser = TomlParser::new(input).map_err(DeserializeError::Parser)?;
85 let mut de = FormatDeserializer::new_owned(parser);
86 // TOML requires deferred mode to handle table reopening
87 de.deserialize_deferred()
88}
89
90/// Deserialize a value from TOML bytes into an owned type.
91///
92/// This is the recommended default for most use cases. The input does not need
93/// to outlive the result, making it suitable for deserializing from temporary
94/// buffers (e.g., HTTP request bodies).
95///
96/// # Errors
97///
98/// Returns an error if the input is not valid UTF-8 or if deserialization fails.
99///
100/// # Example
101///
102/// ```
103/// use facet::Facet;
104/// use facet_toml::from_slice;
105///
106/// #[derive(Facet, Debug, PartialEq)]
107/// struct Config {
108/// name: String,
109/// port: u16,
110/// }
111///
112/// let toml = b"name = \"my-app\"\nport = 8080";
113/// let config: Config = from_slice(toml).unwrap();
114/// assert_eq!(config.name, "my-app");
115/// assert_eq!(config.port, 8080);
116/// ```
117pub fn from_slice<T>(input: &[u8]) -> Result<T, DeserializeError<TomlError>>
118where
119 T: facet_core::Facet<'static>,
120{
121 let s = core::str::from_utf8(input).map_err(|e| {
122 DeserializeError::Parser(TomlError::without_span(TomlErrorKind::InvalidUtf8(e)))
123 })?;
124 from_str(s)
125}
126
127/// Deserialize a value from a TOML string, allowing zero-copy borrowing.
128///
129/// This variant requires the input to outlive the result (`'input: 'facet`),
130/// enabling zero-copy deserialization of string fields as `&str` or `Cow<str>`.
131///
132/// Use this when you need maximum performance and can guarantee the input
133/// buffer outlives the deserialized value. For most use cases, prefer
134/// [`from_str`] which doesn't have lifetime requirements.
135///
136/// # Example
137///
138/// ```
139/// use facet::Facet;
140/// use facet_toml::from_str_borrowed;
141///
142/// #[derive(Facet, Debug, PartialEq)]
143/// struct Config<'a> {
144/// name: &'a str,
145/// port: u16,
146/// }
147///
148/// let toml = r#"
149/// name = "my-app"
150/// port = 8080
151/// "#;
152///
153/// let config: Config = from_str_borrowed(toml).unwrap();
154/// assert_eq!(config.name, "my-app");
155/// assert_eq!(config.port, 8080);
156/// ```
157pub fn from_str_borrowed<'input, 'facet, T>(
158 input: &'input str,
159) -> Result<T, DeserializeError<TomlError>>
160where
161 T: facet_core::Facet<'facet>,
162 'input: 'facet,
163{
164 use facet_format::FormatDeserializer;
165 let parser = TomlParser::new(input).map_err(DeserializeError::Parser)?;
166 let mut de = FormatDeserializer::new(parser);
167 // TOML requires deferred mode to handle table reopening
168 de.deserialize_deferred()
169}
170
171/// Deserialize a value from TOML bytes, allowing zero-copy borrowing.
172///
173/// This variant requires the input to outlive the result (`'input: 'facet`),
174/// enabling zero-copy deserialization of string fields as `&str` or `Cow<str>`.
175///
176/// Use this when you need maximum performance and can guarantee the input
177/// buffer outlives the deserialized value. For most use cases, prefer
178/// [`from_slice`] which doesn't have lifetime requirements.
179///
180/// # Errors
181///
182/// Returns an error if the input is not valid UTF-8 or if deserialization fails.
183///
184/// # Example
185///
186/// ```
187/// use facet::Facet;
188/// use facet_toml::from_slice_borrowed;
189///
190/// #[derive(Facet, Debug, PartialEq)]
191/// struct Config<'a> {
192/// name: &'a str,
193/// port: u16,
194/// }
195///
196/// let toml = b"name = \"my-app\"\nport = 8080";
197/// let config: Config = from_slice_borrowed(toml).unwrap();
198/// assert_eq!(config.name, "my-app");
199/// assert_eq!(config.port, 8080);
200/// ```
201pub fn from_slice_borrowed<'input, 'facet, T>(
202 input: &'input [u8],
203) -> Result<T, DeserializeError<TomlError>>
204where
205 T: facet_core::Facet<'facet>,
206 'input: 'facet,
207{
208 let s = core::str::from_utf8(input).map_err(|e| {
209 DeserializeError::Parser(TomlError::without_span(TomlErrorKind::InvalidUtf8(e)))
210 })?;
211 from_str_borrowed(s)
212}