Skip to main content

ctl_core/input/
entries.rs

1//! The top-level entries of a YAML or TOML text, each with the byte range of
2//! its value, how the value is written and, for a string, its text.
3//!
4//! A caller that reads a mapping it did not write, such as the front matter of
5//! a Markdown file, learns where each value sits and what it reads as, with no
6//! scanner of its own. The format crates decide the types: serde-saphyr for
7//! YAML, which resolves anchors, aliases and tags, and the `toml` crate for
8//! TOML. The spans come from yamled and from the `toml` tree.
9
10use std::ops::Range;
11
12#[cfg(feature = "input-toml")]
13use super::toml_entries;
14#[cfg(feature = "input-yaml")]
15use super::yaml_entries;
16use super::{Format, Input, InputError};
17
18/// How the value of an [`Entry`] is written.
19///
20/// Non-exhaustive: a later release can name another way to write a value.
21/// TOML writes a string four ways, which map onto the first two quoted forms
22/// and the two multi-line ones, and writes a number, a boolean or a date as
23/// [`Written::Plain`].
24#[derive(Clone, Copy, Debug, PartialEq, Eq)]
25#[non_exhaustive]
26pub enum Written {
27    /// A YAML plain scalar, or a TOML number, boolean or date.
28    Plain,
29    /// A single-quoted YAML scalar, or a TOML literal string `'…'`.
30    SingleQuoted,
31    /// A double-quoted YAML scalar, or a TOML basic string `"…"`.
32    DoubleQuoted,
33    /// A YAML literal block scalar, `|`.
34    Literal,
35    /// A YAML folded block scalar, `>`.
36    Folded,
37    /// A TOML multi-line basic string, `"""…"""`.
38    MultilineBasic,
39    /// A TOML multi-line literal string, `'''…'''`.
40    MultilineLiteral,
41    /// A YAML alias, `*name`.
42    Alias,
43    /// A YAML sequence or mapping, or a TOML array or table.
44    Collection,
45}
46
47/// One entry of the top-level mapping or table of a text.
48///
49/// Non-exhaustive: a later release can add a field.
50#[derive(Clone, Debug, PartialEq, Eq)]
51#[non_exhaustive]
52pub struct Entry {
53    /// The key, as the parser read it: quotes and escapes resolved. A YAML key
54    /// that is a number, a boolean or a null is its text as written.
55    pub key: String,
56    /// The bytes of the value in [`Input::text`], as written. A quoted string
57    /// includes its quotes and a block scalar its header. An anchor or a tag
58    /// before a YAML value is outside it, and an alias is `*name`.
59    pub value: Range<usize>,
60    /// How the value is written.
61    pub written: Written,
62    /// The text of the value, when the parser reads it as a string, and `None`
63    /// for a number, a boolean, a null, a date, a collection or an alias to
64    /// one. A YAML alias to a string, and a scalar under `!!str`, hold the
65    /// string.
66    pub string: Option<String>,
67}
68
69impl Input {
70    /// The entries of the text's top-level mapping or table, in the order
71    /// written.
72    ///
73    /// An entry whose key is a YAML sequence, mapping or alias is left out, and
74    /// the entries around it keep their own ranges. A text whose root is no
75    /// mapping, such as a sequence or a scalar, has no entries.
76    ///
77    /// The listing refuses nothing that YAML allows, so a key written twice,
78    /// a merge key (`<<`) and a `.inf` or `.NaN` are entries like the rest,
79    /// where [`Input::parse`] refuses them. The text must pass the limits in
80    /// force, as [`Input::parse`] holds it to, and a YAML input builds the
81    /// document that [`Input::place`] keeps.
82    ///
83    /// ```
84    /// # #[cfg(feature = "input-yaml")]
85    /// # {
86    /// use ctl_core::input::Input;
87    ///
88    /// let input = Input::new("page.yaml", "title: A page\nyear: 2026\n");
89    /// let entries = input.top_level()?;
90    /// let title = &entries[0];
91    /// assert_eq!(title.key, "title");
92    /// assert_eq!(&input.text()[title.value.clone()], "A page");
93    /// assert_eq!(title.string.as_deref(), Some("A page"));
94    /// assert_eq!(entries[1].string, None);
95    /// # }
96    /// # Ok::<(), ctl_core::input::InputError>(())
97    /// ```
98    ///
99    /// # Errors
100    ///
101    /// An [`InputError`] with the parser's problem and its line, as
102    /// [`Input::parse`] gives, for a text that is no YAML or TOML or goes past
103    /// a limit.
104    pub fn top_level(&self) -> Result<Vec<Entry>, InputError> {
105        match self.format {
106            #[cfg(feature = "input-yaml")]
107            Format::Yaml => yaml_entries::entries(self),
108            #[cfg(feature = "input-toml")]
109            Format::Toml => toml_entries::entries(self),
110        }
111    }
112}