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