Skip to main content

tanzim_parse/
lib.rs

1#![doc = include_str!("../README.md")]
2
3mod span;
4
5pub use tanzim_value::{Error, LocatedValue, Value};
6
7pub mod closure;
8
9#[cfg(feature = "env")]
10mod env;
11#[cfg(feature = "json")]
12mod json;
13#[cfg(feature = "toml")]
14mod toml;
15#[cfg(feature = "yaml")]
16mod yaml;
17
18#[cfg(feature = "env")]
19pub use env::Env;
20#[cfg(feature = "json")]
21pub use json::Json;
22#[cfg(feature = "toml")]
23pub use toml::Toml;
24#[cfg(feature = "yaml")]
25pub use yaml::Yaml;
26
27/// Deserializes raw bytes into a [`LocatedValue`] tree for one format.
28///
29/// Implement this to add a new configuration format. Every node in the returned
30/// tree should carry a [`tanzim_value::Location`] that points back to the
31/// source file and line so that downstream error messages can show users exactly
32/// where a bad value came from.
33///
34/// # Auto-detection
35///
36/// When a payload's `format` hint is `None`, the parse stage calls
37/// [`is_format_supported`][Deserialize::is_format_supported] on each registered
38/// parser in order. Return `Some(true)` if confident, `Some(false)` to skip, or `None`
39/// if unsure (another parser may then claim the bytes).
40///
41/// # Example — custom CSV parser
42///
43/// ```rust
44/// use tanzim_parse::{Deserialize, Error, LocatedValue, Value};
45/// use tanzim_value::{Location, Map};
46///
47/// struct CsvParser;
48///
49/// impl Deserialize for CsvParser {
50///     fn name(&self) -> &str { "csv" }
51///     fn supported_format_list(&self) -> Vec<String> { vec!["csv".into()] }
52///     fn is_format_supported(&self, bytes: &[u8]) -> Option<bool> {
53///         Some(bytes.contains(&b','))
54///     }
55///     fn parse(&self, source: &str, resource: &str, bytes: &[u8])
56///         -> Result<LocatedValue, Error>
57///     {
58///         let text = std::str::from_utf8(bytes).map_err(|_| Error::InvalidUtf8 {
59///             location: Location::at(source, resource, None, None, None),
60///         })?;
61///         let mut map = Map::new();
62///         for (line_idx, line) in text.lines().enumerate() {
63///             if let Some((key, val)) = line.split_once(',') {
64///                 let loc = Location::at(source, resource, Some(line_idx + 1), None, None);
65///                 map.insert(key.trim().to_string(), LocatedValue {
66///                     value: Value::String(val.trim().to_string()),
67///                     location: loc,
68///                 });
69///             }
70///         }
71///         let root_loc = Location::at(source, resource, None, None, None);
72///         Ok(LocatedValue { value: Value::Map(map), location: root_loc })
73///     }
74/// }
75/// ```
76pub trait Deserialize {
77    /// Human-readable name used in error messages.
78    fn name(&self) -> &str;
79    /// Format extensions this parser handles (e.g. `["json"]`, `["yml", "yaml"]`).
80    fn supported_format_list(&self) -> Vec<String>;
81    /// Probe `bytes` for auto-detection when `Payload::format` is `None`.
82    ///
83    /// Return `Some(true)` if confident, `Some(false)` if definitely not this format,
84    /// or `None` to abstain (another parser will be tried next).
85    fn is_format_supported(&self, bytes: &[u8]) -> Option<bool>;
86    /// Deserialize `bytes` into a [`LocatedValue`] tree.
87    ///
88    /// `source` is the source kind (e.g. `"file"`) and `resource` is the path or
89    /// identifier; both are used to populate [`tanzim_value::Location`] on every
90    /// node in the returned tree.
91    fn parse(&self, source: &str, resource: &str, bytes: &[u8]) -> Result<LocatedValue, Error>;
92}