pub struct Input { /* private fields */ }input-toml or input-yaml only.Expand description
A named text, ready to parse and validate.
The name is usually the file’s path, and appears in every problem. Build
one with Input::new_with_format for text in memory, or Input::open
for a file. With input-yaml, Input::new and Input::read build a YAML
one.
Implementations§
Source§impl Input
impl Input
Sourcepub fn top_level(&self) -> Result<Vec<Entry>, InputError>
pub fn top_level(&self) -> Result<Vec<Entry>, InputError>
The entries of the text’s top-level mapping or table, in the order written.
An entry whose key is a YAML sequence, mapping or alias is left out, and the entries around it keep their own ranges. A text whose root is no mapping, such as a sequence or a scalar, has no entries.
The listing refuses nothing that YAML allows, so a key written twice,
a merge key (<<) and a .inf or .NaN are entries like the rest,
where Input::parse refuses them. The text must pass the limits in
force, as Input::parse holds it to, and a YAML input builds the
document that Input::place keeps.
use ctl_core::input::Input;
let input = Input::new("page.yaml", "title: A page\nyear: 2026\n");
let entries = input.top_level()?;
let title = &entries[0];
assert_eq!(title.key, "title");
assert_eq!(&input.text()[title.value.clone()], "A page");
assert_eq!(title.string.as_deref(), Some("A page"));
assert_eq!(entries[1].string, None);§Errors
An InputError with the parser’s problem and its line, as
Input::parse gives, for a text that is no YAML or TOML or goes past
a limit.
Source§impl Input
impl Input
Sourcepub fn new(name: impl Into<String>, text: impl Into<String>) -> Self
Available on crate feature input-yaml only.
pub fn new(name: impl Into<String>, text: impl Into<String>) -> Self
input-yaml only.A YAML input from text already in memory, whatever the extension of
name. Set another format with Input::format.
Sourcepub fn new_with_format(
name: impl Into<String>,
text: impl Into<String>,
format: Format,
) -> Self
pub fn new_with_format( name: impl Into<String>, text: impl Into<String>, format: Format, ) -> Self
An input of format from text already in memory, whatever the
extension of name. It needs only format’s feature, so a build
without YAML builds text in memory with it.
Sourcepub fn read(path: &Path) -> Result<Self, InputError>
Available on crate feature input-yaml only.
pub fn read(path: &Path) -> Result<Self, InputError>
input-yaml only.Read a YAML file, named by its path, whatever its extension.
§Errors
An InputError with the I/O failure.
Sourcepub fn open(path: &Path) -> Result<Self, InputError>
pub fn open(path: &Path) -> Result<Self, InputError>
Read a file and take its Format from its extension, as
Format::of does. The extension is checked before the file is read.
§Errors
An InputError for an extension Format::of refuses, or with the
I/O failure.
Sourcepub fn format(self, format: Format) -> Self
pub fn format(self, format: Format) -> Self
Set how the text is written, over what the constructor took.
Sourcepub fn frontmatter(&self) -> Option<(Self, &str)>
Available on crate feature input-yaml only.
pub fn frontmatter(&self) -> Option<(Self, &str)>
input-yaml only.Split YAML frontmatter from a Markdown body.
The frontmatter is the text between a first line of --- and the next
line of ---. It is YAML whatever this input’s format, and problems in
it keep the line numbers of the whole file. The second item is the body
after the closing line. Returns None when the text does not start with
a --- line, or never closes it.
Sourcepub fn limits(self, limits: Limits) -> Self
pub fn limits(self, limits: Limits) -> Self
Override limits for this input alone, above every other tier. See
Limits for the order.
Sourcepub fn flattened(self, field: FieldPath) -> Self
Available on crate feature validate only.
pub fn flattened(self, field: FieldPath) -> Self
validate only.Declare that the schema field at field is flattened into its parent
with #[serde(flatten)], so the file writes its entries directly under
the parent.
garde names the field in each problem’s path all the same, and serde
gives no sign of the flattening at run time, so the schema declares it.
Input::check then drops the field’s step from each garde path whose
leading steps are field, before it places the problem and prints its
field. field is written in the schema’s terms, the way garde names it,
and each call adds one field. A declaration that matches no garde path
changes nothing. Input::place, Input::place_key, and
Input::problem_at take paths in the file’s terms, so a declaration
does not touch them.
§Examples
verctl’s assets holds its targets in a flattened map field targets.
Without the declaration, the problem prints as
assets.targets.linux-x64.files and sits on the line below assets:.
use std::collections::BTreeMap;
use ctl_core::input::{FieldPath, Input};
use ctl_core::validate::at_least_one;
use garde::Validate;
use serde::Deserialize;
#[derive(Debug, Deserialize, Validate)]
struct Config {
#[garde(dive)]
assets: Assets,
}
#[derive(Debug, Deserialize, Validate)]
struct Assets {
#[serde(flatten)]
#[garde(dive)]
targets: BTreeMap<String, Target>,
}
#[derive(Debug, Deserialize, Validate)]
struct Target {
#[garde(custom(at_least_one("file")))]
files: Vec<String>,
}
let error = Input::new("ver.yaml", "assets:\n linux-x64:\n files: []\n")
.flattened(FieldPath::root().key("assets").key("targets"))
.load::<Config>()
.unwrap_err();
assert_eq!(
error.to_string(),
"ver.yaml:3:12: assets.linux-x64.files: must declare at least one file"
);Sourcepub fn parse<T: DeserializeOwned>(&self) -> Result<T, InputError>
pub fn parse<T: DeserializeOwned>(&self) -> Result<T, InputError>
Parse the text into T, without validating it.
In YAML only true and false are booleans, and a duplicate key or a
merge key (<<) is refused. In TOML the format itself refuses a
duplicate key.
The text must fit the limits in force, decided field by field through
the tiers Limits lists. TOML reads only max_reader_input_bytes,
max_depth, and max_nodes, because the other fields name YAML
syntax.
§Errors
An InputError with the parser’s problem and its line. A limit
problem names the limit, its value, and where to raise it, or, without
input-yaml, that the tool sets it.
Sourcepub fn check<T: Validate + ?Sized>(
&self,
value: &T,
context: &T::Context,
) -> Result<(), InputError>
Available on crate feature validate only.
pub fn check<T: Validate + ?Sized>( &self, value: &T, context: &T::Context, ) -> Result<(), InputError>
validate only.Validate a parsed value against context.
Each of garde’s problems lands on the line of the field it names, or
of its nearest written ancestor. The place comes from the parts of
garde’s path through Input::place, so a key that holds a . is
found as one key. Problem::field is garde’s printed path, for display
only. A path through a field Input::flattened declares loses that
field’s step, and its Problem::field is the shorter path as
FieldPath prints it.
§Errors
An InputError with every problem garde reports.
Sourcepub fn place(&self, path: &FieldPath) -> Option<Place>
pub fn place(&self, path: &FieldPath) -> Option<Place>
Where path is written in the text, or where its nearest written
ancestor is.
The line counts the whole file, so a frontmatter input adds the lines
before it. A path the text lacks gives the place of the last step that
exists, and a caller that needs an exact hit compares the result with
the place of the path’s parent. None when the text does not parse.
None too when the text goes past the limits in force, as
Input::parse would refuse it, so placing never parses a text the
limits keep out. A YAML input checks max_reader_input_bytes,
max_depth and max_nodes before it hands the text to yamled, which
has no budget. A TOML input checks max_reader_input_bytes, and
the parse of its tree is the one Input::parse does first.
A YAML input parses its text for the first placing and keeps the
result, so Input::check and later calls read one document. A TOML
input parses its text on each call.
use ctl_core::input::{FieldPath, Input};
let input = Input::new("app.yaml", "files:\n 'docs/**/*.md':\n label: ''\n");
let path = FieldPath::root()
.key("files")
.key("docs/**/*.md")
.key("label");
let at = input.place(&path).map(|at| (at.line, at.column));
assert_eq!(at, Some((3, 12)));Sourcepub fn place_key(&self, path: &FieldPath) -> Option<Place>
pub fn place_key(&self, path: &FieldPath) -> Option<Place>
Where the last step of path is written as a key, or where its nearest
written ancestor’s key is.
Input::place gives where a value starts. For a scalar or a flow
value that is the line of the key, and for a block mapping or a block
list it is the first line below it. A fault about the key itself, such
as a key that names nothing, belongs on the key, and this finds it.
A step that is an index has no key, and neither has the root, so they
give the place of their value, as place does. A path the text lacks
gives the key of the last step that exists. The line counts the whole
file, None is for a text that does not parse or goes past the limits,
and a YAML document is built once and shared with Input::place.
use ctl_core::input::{FieldPath, Input};
let input = Input::new("app.yaml", "files:\n 'docs/**/*.md':\n label: ''\n");
let glob = FieldPath::root().key("files").key("docs/**/*.md");
let value = input.place(&glob).map(|at| (at.line, at.column));
let key = input.place_key(&glob).map(|at| (at.line, at.column));
assert_eq!(value, Some((3, 5)));
assert_eq!(key, Some((2, 3)));Sourcepub fn problem_at(
&self,
path: &FieldPath,
message: impl Into<String>,
) -> Problem
pub fn problem_at( &self, path: &FieldPath, message: impl Into<String>, ) -> Problem
A Problem for path with message, placed as Input::place
places it, with the path printed as garde prints a field.
A CLI uses it for a check that runs after the read, such as one that needs a file the config names.
Sourcepub fn load<T>(&self) -> Result<T, InputError>
Available on crate feature validate only.
pub fn load<T>(&self) -> Result<T, InputError>
validate only.Parse the text into T, then validate it with T’s default context.
§Errors
As Input::parse and Input::check.