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.
54    pub key: String,
55    /// The bytes of the value in [`Input::text`], as written. A quoted string
56    /// includes its quotes and a block scalar its header. An anchor or a tag
57    /// before a YAML value is outside it, and an alias is `*name`.
58    pub value: Range<usize>,
59    /// How the value is written.
60    pub written: Written,
61    /// The text of the value, when the parser reads it as a string, and `None`
62    /// for a number, a boolean, a null, a date, a collection or an alias to
63    /// one. A YAML alias to a string, and a scalar under `!!str`, hold the
64    /// string.
65    pub string: Option<String>,
66}
67
68impl Input {
69    /// The entries of the text's top-level mapping or table, in the order
70    /// written.
71    ///
72    /// An entry whose key is not a string, such as a YAML key that is a
73    /// number, a sequence or an alias, is left out, and the entries around it
74    /// keep their own ranges. A text whose root is no mapping, such
75    /// as a sequence or a scalar, has no entries. The text must pass the limits
76    /// in force, as [`Input::parse`] holds it to, and a YAML input builds the
77    /// document that [`Input::place`] keeps.
78    ///
79    /// ```
80    /// # #[cfg(feature = "input-yaml")]
81    /// # {
82    /// use ctl_core::input::Input;
83    ///
84    /// let input = Input::new("page.yaml", "title: A page\nyear: 2026\n");
85    /// let entries = input.top_level()?;
86    /// let title = &entries[0];
87    /// assert_eq!(title.key, "title");
88    /// assert_eq!(&input.text()[title.value.clone()], "A page");
89    /// assert_eq!(title.string.as_deref(), Some("A page"));
90    /// assert_eq!(entries[1].string, None);
91    /// # }
92    /// # Ok::<(), ctl_core::input::InputError>(())
93    /// ```
94    ///
95    /// # Errors
96    ///
97    /// An [`InputError`] with the parser's problem and its line, as
98    /// [`Input::parse`] gives, including a duplicate key, a YAML merge key and
99    /// a text past a limit.
100    pub fn top_level(&self) -> Result<Vec<Entry>, InputError> {
101        match self.format {
102            #[cfg(feature = "input-yaml")]
103            Format::Yaml => yaml_entries::entries(self),
104            #[cfg(feature = "input-toml")]
105            Format::Toml => toml_entries::entries(self),
106        }
107    }
108}