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}