pub struct Document { /* private fields */ }std only.Expand description
A YAML document with byte-faithful source preservation, typed data access, and path-targeted edits.
Document carries three coordinated views of the same input:
an immutable green tree that reproduces the source byte-for-byte,
a typed Value for data access, and an internal span tree
that maps any Value-shaped path back to a byte range. Edits
flow through Document::replace_span (the primitive) and
Document::set (the path-shaped wrapper); untouched bytes —
indentation, comments, blank lines, sibling entries — are
preserved verbatim.
Every path-taking method reads the grammar described in
crate::path: server.port, items[0].name, and, for a key the
grammar would otherwise read as structure, a bracket-quoted segment
such as labels["app.kubernetes.io/name"].
§Examples
Read-only round-trip:
use noyalib::cst::parse_document;
let src = "name: noyalib # the project\nversion: 0.0.1\n";
let doc = parse_document(src).unwrap();
assert_eq!(doc.to_string(), src);Path-targeted edit:
use noyalib::cst::parse_document;
let mut doc = parse_document("name: foo\nversion: 0.0.1\n").unwrap();
doc.set("version", "0.0.2").unwrap();
assert_eq!(doc.to_string(), "name: foo\nversion: 0.0.2\n");Implementations§
Source§impl Document
impl Document
Sourcepub fn anchors(&self) -> Vec<AnchorInfo>
pub fn anchors(&self) -> Vec<AnchorInfo>
Every &name declaration in this document, in source order.
§Examples
use noyalib::cst::parse_document;
let doc = parse_document(
"defaults: &cfg\n port: 8080\nserver:\n <<: *cfg\n",
).unwrap();
let anchors = doc.anchors();
assert_eq!(anchors.len(), 1);
assert_eq!(anchors[0].name, "cfg");Sourcepub fn aliases(&self) -> Vec<AliasInfo>
pub fn aliases(&self) -> Vec<AliasInfo>
Every *name reference in this document, in source order.
§Examples
use noyalib::cst::parse_document;
let doc = parse_document("a: &x 1\nb: *x\nc: *x\n").unwrap();
let aliases = doc.aliases();
assert_eq!(aliases.len(), 2);Sourcepub fn aliases_of(&self, name: &str) -> Vec<AliasInfo>
pub fn aliases_of(&self, name: &str) -> Vec<AliasInfo>
Aliases whose name matches name, in source order.
§Examples
use noyalib::cst::parse_document;
let doc = parse_document("a: &x 1\nb: &y 2\nc: *x\nd: *y\n").unwrap();
let xs = doc.aliases_of("x");
assert_eq!(xs.len(), 1);
assert_eq!(xs[0].name, "x");Sourcepub fn materialise_alias_at(&mut self, position: usize) -> Result<()>
pub fn materialise_alias_at(&mut self, position: usize) -> Result<()>
Replace the *name alias whose mark begins at byte
position with the source text of the matching &name’s
scalar value.
After the splice, the alias’s site holds an independent copy of the anchored scalar — subsequent edits to the anchored value do not propagate to it.
§Errors
positiondoes not start an*nametoken.- The named anchor is not declared earlier in source order.
- The anchored value is not a scalar (multi-line block
collections require manual handling — read the anchor’s
span via
Self::anchorsand splice withSelf::replace_span). - The same parse-after-edit errors as
Self::replace_span.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("a: &x 7\nb: *x\n").unwrap();
let alias = doc.aliases()[0].clone();
doc.materialise_alias_at(alias.mark_span.0).unwrap();
assert!(!doc.to_string().contains("*x"));
assert!(doc.to_string().contains("b: 7"));Sourcepub fn materialise_aliases_of(&mut self, name: &str) -> Result<usize>
pub fn materialise_aliases_of(&mut self, name: &str) -> Result<usize>
Materialise every alias whose name matches name. Returns
the count of aliases replaced.
Aliases are processed in reverse source order so each splice’s offsets stay valid for later (earlier in source) aliases.
§Errors
As Self::materialise_alias_at. The first failing alias
aborts the batch — already-materialised aliases stay
materialised, the rest are unchanged.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("a: &x 7\nb: *x\nc: *x\n").unwrap();
let n = doc.materialise_aliases_of("x").unwrap();
assert_eq!(n, 2);
assert!(!doc.to_string().contains('*'));Sourcepub fn rename_anchor(&mut self, old: &str, new: &str) -> Result<usize>
pub fn rename_anchor(&mut self, old: &str, new: &str) -> Result<usize>
Rename every &old anchor declaration and every *old
alias reference to new in one atomic pass. Returns the
total number of touched sites (anchors + aliases).
Splices run in reverse source order so each successive splice’s offsets stay valid for earlier sites. The whole rename is byte-faithful outside the touched marks — comments, blank lines, and sibling formatting survive verbatim.
§Errors
newis empty or contains characters that would not be accepted as a YAML anchor name (any of the flow indicators,[]{}or whitespace per YAML 1.2 §6.9.2).olddoes not match any anchor or alias in the document (so the call is a no-op the user probably did not intend) — surfaced as an error rather than a silent zero-count.newalready names a different anchor in the document (unlessnew == old): merging the two would make every*newalias resolve to the last declaration, silently changing the document’s meaning, so the rename is refused.- The same parse-after-edit errors as
crate::cst::Document::replace_span. The rename is a single atomic splice over the whole document, so it is all-or-nothing: on any error the document is left byte-for-byte unchanged.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document(
"defaults: &cfg\n port: 8080\nservice:\n <<: *cfg\nbackup: *cfg\n",
).unwrap();
// Rename `cfg` → `defaults`. The single `&cfg` declaration
// and both `*cfg` references are updated in one call.
let n = doc.rename_anchor("cfg", "defaults").unwrap();
assert_eq!(n, 3); // 1 anchor + 2 aliases
let out = doc.to_string();
assert!(!out.contains("&cfg"));
assert!(!out.contains("*cfg"));
assert!(out.contains("&defaults"));
assert!(out.contains("*defaults"));Source§impl Document
impl Document
Sourcepub fn comments_at(&self, path: &str) -> CommentBundle
pub fn comments_at(&self, path: &str) -> CommentBundle
Comments decorating the node at path, classified by position.
Returns an empty CommentBundle when path does not
resolve. Path syntax matches Document::span_at —
foo.bar, items[0], items[0].name. Wildcard /
recursive-descent segments are not supported (a non-singular
span has no canonical “above” line).
§Examples
use noyalib::cst::parse_document;
let src = "# A multi-line\n# leading block\nport: 8080 # inline\n";
let doc = parse_document(src).unwrap();
let b = doc.comments_at("port");
assert_eq!(b.before.len(), 2);
assert_eq!(b.inline.as_ref().unwrap().text, " inline");Sourcepub fn set_inline_comment(&mut self, path: &str, text: &str) -> Result<()>
pub fn set_inline_comment(&mut self, path: &str, text: &str) -> Result<()>
Set (or replace) the inline comment on the single-line node
at path — the #-introduced comment that follows the value on
the same line.
text is the comment body without the leading #; it renders as
# <text> (a single space after #, or a bare # when text
is empty). If the node already has an inline comment, its body is
replaced in place, keeping the existing separating whitespace;
otherwise # <text> is appended after the value.
Guarded like the other mutators: the edit must re-parse and leave the document’s typed value unchanged (a comment carries no data), or it is rolled back.
§Errors
pathdoes not resolve to a node.- The node spans multiple lines — it has no inline comment of its own; comment its entries instead.
textcontains a newline (a comment is a single line).- The splice would not re-parse or would change data (roll back).
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("port: 8080\n").unwrap();
doc.set_inline_comment("port", "the listen port").unwrap();
assert_eq!(doc.source(), "port: 8080 # the listen port\n");
doc.set_inline_comment("port", "changed").unwrap();
assert_eq!(doc.source(), "port: 8080 # changed\n");Sourcepub fn remove_inline_comment(&mut self, path: &str) -> Result<()>
pub fn remove_inline_comment(&mut self, path: &str) -> Result<()>
Remove the inline comment on the node at path, if any,
taking the separating whitespace with it so no trailing space is
left. A no-op returning Ok(()) when the node has no inline
comment (or the path does not resolve).
Guarded and rolled back exactly like
set_inline_comment.
§Errors
- The removal would not re-parse or would change data (rolls back). A missing comment or path is a no-op, not an error.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("port: 8080 # noise\n").unwrap();
doc.remove_inline_comment("port").unwrap();
assert_eq!(doc.source(), "port: 8080\n");Sourcepub fn set_leading_comment(&mut self, path: &str, text: &str) -> Result<()>
pub fn set_leading_comment(&mut self, path: &str, text: &str) -> Result<()>
Set (or replace) the leading comment block above the
single-line mapping entry at path — the run of comment lines
that comments_at(path).before reports.
text becomes one comment line per \n-separated segment, each
rendered at the key’s indentation as # <segment> (a bare #
for an empty segment). An existing leading block is replaced in
place; otherwise the block is inserted immediately above the
entry’s line.
Scope: block mapping keys on a single line (where the key
token, and therefore the entry’s own line and indent, are
unambiguous). Multi-line / nested entries and sequence items are
a follow-up — comments_at does not attribute a leading block to
them unambiguously. Guarded like the other mutators: the edit
must re-parse and leave the typed value unchanged, or it rolls
back.
§Errors
pathdoes not address a single-line block-mapping key.- The splice would not re-parse or would change data (roll back).
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("port: 8080\n").unwrap();
doc.set_leading_comment("port", "the listen port").unwrap();
assert_eq!(doc.source(), "# the listen port\nport: 8080\n");
doc.set_leading_comment("port", "line one\nline two").unwrap();
assert_eq!(doc.source(), "# line one\n# line two\nport: 8080\n");Sourcepub fn remove_leading_comment(&mut self, path: &str) -> Result<()>
pub fn remove_leading_comment(&mut self, path: &str) -> Result<()>
Remove the leading comment block above the mapping entry at
path, if any. A no-op returning Ok(()) when there is none (or
the path does not address a single-line mapping key).
Guarded and rolled back exactly like
set_leading_comment.
§Errors
- The removal would not re-parse or would change data (rolls back).
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("# noise\n# more\nport: 8080\n").unwrap();
doc.remove_leading_comment("port").unwrap();
assert_eq!(doc.source(), "port: 8080\n");Source§impl Document
impl Document
Sourcepub fn set_comment(
&mut self,
path: &str,
position: CommentPosition,
text: &str,
) -> Result<()>
pub fn set_comment( &mut self, path: &str, position: CommentPosition, text: &str, ) -> Result<()>
Set the comment at path in the given position.
Replaces an existing comment, or writes a new one when there is
none. text is the comment body without the leading #; a
single leading space is added when text does not already begin
with whitespace, so set_comment(p, Inline, "note") yields
# note.
For CommentPosition::Before, a text containing newlines
becomes one # line per line, each at the node’s own
indentation.
The edit goes through Document::replace_span, so it inherits
the same guard: an edit that would make the document re-parse
differently is rejected rather than written.
§Errors
pathdoes not resolve to a node.- The resulting document would not re-parse to the same value.
§Examples
use noyalib::cst::{parse_document, CommentPosition};
let mut doc = parse_document("port: 8080\n").unwrap();
doc.set_comment("port", CommentPosition::Inline, "listen port").unwrap();
assert_eq!(doc.source(), "port: 8080 # listen port\n");Sourcepub fn remove_comment(
&mut self,
path: &str,
position: CommentPosition,
) -> Result<()>
pub fn remove_comment( &mut self, path: &str, position: CommentPosition, ) -> Result<()>
Remove the comment at path in the given position.
A no-op when there is no comment there. For
CommentPosition::Inline the whitespace separating the
comment from the node’s content goes with it, so no trailing
spaces are left behind. For CommentPosition::Before the
whole run of comment lines is removed.
§Errors
§Examples
use noyalib::cst::{parse_document, CommentPosition};
let mut doc = parse_document("port: 8080 # note\n").unwrap();
doc.remove_comment("port", CommentPosition::Inline).unwrap();
assert_eq!(doc.source(), "port: 8080\n");Source§impl Document
impl Document
Sourcepub fn as_value(&self) -> Ref<'_, Value>
pub fn as_value(&self) -> Ref<'_, Value>
Borrow the typed Value view of the document.
On the first call after an edit (or a fresh parse), this
triggers a one-shot parse of the current source into the
internal Value / SpanTree cache. Subsequent calls on the
same document are O(1) until the next edit invalidates the
cache. Code that batches many edits without reading the
typed view in between never pays the typed-tree cost.
§Examples
use noyalib::cst::parse_document;
let doc = parse_document("name: noyalib\n").unwrap();
assert_eq!(doc.as_value()["name"].as_str(), Some("noyalib"));Sourcepub fn source(&self) -> &str
pub fn source(&self) -> &str
The original source bytes for this document. After an edit reflects the current source.
§Examples
use noyalib::cst::parse_document;
let src = "key: 1\n";
let doc = parse_document(src).unwrap();
assert_eq!(doc.source(), src);Sourcepub fn span_at(&self, path: &str) -> Option<(usize, usize)>
pub fn span_at(&self, path: &str) -> Option<(usize, usize)>
Resolve a path to the byte range of the value at that path,
if any.
Path syntax matches the rest of the crate (foo.bar,
items[0], items[0].name). Wildcard / recursive-descent
segments are not supported here — they have no single span.
A duplicated mapping key resolves to its last occurrence,
the same occurrence the typed view keeps (as_value loads
with the default DuplicateKeyPolicy::Last, the YAML 1.2
behaviour) — the returned span always denotes the node that
as_value selects for the path.
§Examples
use noyalib::cst::parse_document;
let doc = parse_document("name: noyalib\nversion: 0.0.1\n").unwrap();
let (s, e) = doc.span_at("version").unwrap();
assert_eq!(&doc.source()[s..e], "0.0.1");A duplicate key resolves to the occurrence the typed view keeps:
use noyalib::cst::parse_document;
let doc = parse_document("k: one\nk: two\n").unwrap();
let (s, e) = doc.span_at("k").unwrap();
assert_eq!(&doc.source()[s..e], "two");
assert_eq!(doc.get("k"), Some("two"));Sourcepub fn key_span(&self, path: &str) -> Option<(usize, usize)>
pub fn key_span(&self, path: &str) -> Option<(usize, usize)>
Return the byte span of a mapping entry’s key token, the
read-only companion to span_at (which returns
the value span). source()[start..end] is the key exactly as
written — quotes included for a quoted key.
This exposes, read-only, the same key site
rename_key rewrites; it is the span tooling
needs to report duplicate keys with positions or to drive a
“rename key” code action without walking the green tree by hand.
Returns None when the path does not resolve to a block-mapping
entry with a simple scalar key — a sequence index, an alias
(*name) site (which owns no key bytes of its own), a key
produced by a << merge, or a path that does not resolve at all.
use noyalib::cst::parse_document;
let doc = parse_document("name: foo\n\"quoted key\": 1\n").unwrap();
let (s, e) = doc.key_span("name").unwrap();
assert_eq!(&doc.source()[s..e], "name");
let (s, e) = doc.key_span("quoted key").unwrap();
assert_eq!(&doc.source()[s..e], "\"quoted key\"");
assert_eq!(doc.key_span("missing"), None);Sourcepub fn validate(&self) -> Result<()>
pub fn validate(&self) -> Result<()>
Verify that the current source re-parses cleanly.
Document::set (and the rest of the path-shaped edit API)
uses a localised-repair fast path that gates each splice on
the fragment’s own scanner-level validation but commits
optimistically: a structurally invalid splice across the
whole document — for example, a value like [ that opens a
flow collection never closed at end-of-input — passes the
fragment check and only surfaces when the typed view is
next read. as_value, span_at, get, and any path-shaped
API panic on first access in that state.
validate is the non-panicking eager check: call it after
an edit (or before handing the document to a downstream
consumer) to surface any document-level parse error as a
regular Result. On success, the typed cache is populated
as a side-effect so a subsequent as_value call is free.
§Errors
Returns the underlying parse error if the source no longer parses as a single YAML document.
§Examples
Eagerly validate after an edit that may not be safe:
use noyalib::cst::parse_document;
let mut doc = parse_document("name: foo\n").unwrap();
// `[` opens a flow seq that is never closed — the local
// repair commits optimistically, but the document is now
// structurally broken. `validate` surfaces that as an
// error rather than waiting for the next typed-view read.
doc.set("name", "[").unwrap();
assert!(doc.validate().is_err());Validate a freshly-parsed document — always succeeds:
use noyalib::cst::parse_document;
let doc = parse_document("name: foo\n").unwrap();
assert!(doc.validate().is_ok());Sourcepub fn get(&self, path: &str) -> Option<&str>
pub fn get(&self, path: &str) -> Option<&str>
Return the source slice of the value at path.
§Examples
use noyalib::cst::parse_document;
let doc = parse_document("items:\n - one\n - two\n").unwrap();
assert_eq!(doc.get("items[1]"), Some("two"));Sourcepub fn replace_span(
&mut self,
start: usize,
end: usize,
replacement: &str,
) -> Result<()>
pub fn replace_span( &mut self, start: usize, end: usize, replacement: &str, ) -> Result<()>
Replace the bytes in start..end with replacement and
re-parse. The caller is responsible for replacement being a
syntactically valid fragment in that position; if the spliced
source fails to parse, the original document is left
unchanged and the parse error is returned.
§Errors
Error::Parseif the resulting source is not valid YAML.Error::Parseifstart..endis out of bounds or not a character boundary.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("a: 1\n").unwrap();
let (s, e) = doc.span_at("a").unwrap();
doc.replace_span(s, e, "42").unwrap();
assert_eq!(doc.to_string(), "a: 42\n");Sourcepub fn last_repair_scope(&self) -> Option<RepairScope>
pub fn last_repair_scope(&self) -> Option<RepairScope>
Last successful repair scope, if any. Useful for tests and
instrumentation; returns None for a freshly-parsed
document or when the most recent edit fell back to a full
re-parse.
Sourcepub fn set(&mut self, path: &str, fragment: &str) -> Result<()>
pub fn set(&mut self, path: &str, fragment: &str) -> Result<()>
Replace the value at path with fragment.
fragment is spliced verbatim into the source — the caller
supplies the YAML representation. This deliberately matches no
scalar style automatically; choose double-quoted, plain, or
block style to suit.
§Prefer Document::set_value for values
Verbatim means the fragment is YAML, not text. set(p, "true")
writes the boolean, set(p, "") writes null, and
set(p, "v # x") writes v with a comment after it. If you
have a value rather than a spelling, set_value renders it
— quoting, escaping and choosing a block style as needed — so
it reads back as exactly what you passed in.
§The fragment cannot reach outside path
A fragment containing a newline could previously give the document new entries:
set("a", "v\nc: 3") on "a: 1\nb: 2\n" -> a: v
c: 3
b: 2The re-parse guard could not catch that, because the result is
valid YAML. An oracle now checks that restoring the original
value at path reproduces the original document; if the
fragment changed anything elsewhere, the edit is refused and the
document is left untouched. Restructuring the target itself —
scalar to mapping, say — remains allowed.
§Errors
Error::Parse(...)with “path not found” ifpathdoes not resolve in the current document.- The same errors as
Document::replace_spanotherwise.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("name: foo\nversion: 0.0.1\n").unwrap();
doc.set("version", "0.0.2").unwrap();
assert_eq!(doc.to_string(), "name: foo\nversion: 0.0.2\n");Sourcepub fn set_value(&mut self, path: &str, value: &Value) -> Result<()>
pub fn set_value(&mut self, path: &str, value: &Value) -> Result<()>
Replace the value at path with a typed Value, formatting
the YAML fragment to match the existing scalar style at the
target site.
Style matching:
PlainScalar— emit plain when safe, double-quoted otherwise.SingleQuotedScalar— wrap in'…'(only string values).DoubleQuotedScalar— wrap in"…"with standard escapes (only string values).LiteralScalar/FoldedScalar— a single-line replacement is emitted plain (or quoted when unsafe); a multi-line one is re-emitted as a literal block when representable, and refused otherwise. Folded style is not yet reproduced — a changed value at a>site comes back|or plain.
Setting a value equal to the one already loaded is a no-op:
the source is left byte-identical, so the author’s spelling
(1.10, 0x1F, ~, an implicit null, a >- folded scalar)
survives a save that does not change the value. Equality is
Value’s own, so whether 1.0 over a loaded 1 is a no-op
follows Number’s PartialEq for the active features.
Non-string values (numbers, booleans, null) are emitted plain regardless of the existing style — quoting them would change the parsed type round-trip.
Inside a […] / {…} flow collection the same styles apply,
except that a plain spelling is also refused when the string
contains , [ ] { or } (structural anywhere in flow
context), and a multi-line string is double-quoted with \n
escapes, because block scalars do not exist in flow context:
use noyalib::cst::parse_document;
use noyalib::Value;
let mut doc = parse_document("m: {a: 1, b: 2}\n").unwrap();
doc.set_value("m.a", &Value::String("x, y".into())).unwrap();
assert_eq!(doc.to_string(), "m: {a: \"x, y\", b: 2}\n");
doc.set_value("m.b", &Value::String("two\nlines".into())).unwrap();
assert_eq!(doc.to_string(), "m: {a: \"x, y\", b: \"two\\nlines\"}\n");§Filling in an implicit null
An absent block-mapping value (a:) or empty sequence item (- ) has
no bytes to replace, so the value is inserted after the : / -
instead — before any comment on the line, and with no style to inherit,
so the neighbour rule above decides the spelling on its own.
span_at still reports None there: the node has
nothing to read, which is a separate question from where a write goes.
§A trailing comment beside a new block literal
A multi-line string is written as a literal block scalar, which
runs to the end of its last content line. A comment that trailed
the old one-line value (title: Hello # note) therefore moves to
the block scalar’s header line, where YAML permits one:
use noyalib::cst::parse_document;
use noyalib::Value;
let mut doc = parse_document("title: Hello # note\n").unwrap();
doc.set_value("title", &Value::String("multi\nline".into())).unwrap();
assert_eq!(doc.to_string(), "title: |- # note\n multi\n line\n");
assert_eq!(doc.as_value()["title"].as_str(), Some("multi\nline"));§Collections (#328)
A Value::Sequence / Value::Mapping replaces an existing
collection node in that node’s own style — flow stays flow
(tags: [a, b] set to [a, c] emits tags: [a, c]), block
stays block at the old value’s column. The splice is verified
to load back as the document with exactly this path replaced,
or it is rolled back. Replacing a scalar with a collection is
still refused: a value that must move onto its own lines is a
layout decision set expresses with a fragment.
use noyalib::cst::parse_document;
use noyalib::{Value, from_str};
let mut doc = parse_document("tags: [a, b]\nname: x\n").unwrap();
let tags: Value = from_str("[a, c]").unwrap();
doc.set_value("tags", &tags).unwrap();
assert_eq!(doc.to_string(), "tags: [a, c]\nname: x\n");§Anchored nodes (#338)
A write into a value that *name alias sites share lands at
every one of them, so it is refused — the same policy
rename_key, remove and
the inserters follow. Call
materialise_aliases_of first
to give each site its own copy. Setting a value equal to
the current one stays a no-op wherever it points.
§Errors
- Path not found.
- The target sits inside an anchored value with live alias references.
- Target is a block scalar being replaced by a multi-line string it cannot represent.
- Caller passed a
Sequence/Mappingand the target is a scalar (usesetwith a pre-formatted fragment to grow a scalar into a collection). - The same errors as
Document::replace_spanotherwise.
§Examples
use noyalib::cst::parse_document;
use noyalib::Value;
let mut doc = parse_document("name: noyalib\nversion: 0.0.1\n").unwrap();
doc.set_value("version", &Value::String("0.0.2".into())).unwrap();
assert_eq!(doc.to_string(), "name: noyalib\nversion: 0.0.2\n");
// An equal value does not touch the bytes.
let mut doc = parse_document("ratio: 1.10\n").unwrap();
doc.set_value("ratio", &Value::from(1.1_f64)).unwrap();
assert_eq!(doc.to_string(), "ratio: 1.10\n");Sourcepub fn set_path(&mut self, path: &str, value: &Value) -> Result<()>
pub fn set_path(&mut self, path: &str, value: &Value) -> Result<()>
Like set_value, but creates every missing
mapping level along path on the way (#327, ADR-0009).
A frontmatter writer setting menu.visible must not care
whether menu: exists yet — the writer it replaces creates it.
set_path resolves the deepest existing ancestor and:
- whole path exists — behaves exactly like
set_value(upsert, equal-value no-op, scalar-only replacement); - a block-mapping ancestor exists — inserts the remaining
chain through
insert_entry_value, which owns the indentation, quoting, and the typed-oracle guard; - the document is empty (nothing but comments, blank lines,
or a bare
---) — appends the rendered chain after the existing bytes, so a comment header survives its document’s first key.
The style machinery is the same one every *_value mutator
uses: quoting stays with Emit, new levels indent at the
document’s indent_unit, and the edit is
verified to change exactly the addressed path before it is
kept.
§Errors
- An existing path segment resolves to a scalar (
title.xwheretitleis a string) or to a null value other than the empty document root; the source is left byte-identical. - A missing segment is a sequence index —
set_pathcreates mappings, never sequence items. - The nearest existing ancestor is a flow collection or an
empty
{}— the flow inserters are tracked by #338; the refusal is clean. - The same errors as
set_value/insert_entry_valueotherwise.
§Examples
use noyalib::cst::parse_document;
use noyalib::Value;
// Creates the missing `menu:` level.
let mut doc = parse_document("title: x\n").unwrap();
doc.set_path("menu.visible", &Value::Bool(true)).unwrap();
assert_eq!(doc.to_string(), "title: x\nmenu:\n visible: true\n");
// An empty document receives its first key.
let mut doc = parse_document("").unwrap();
doc.set_path("menu.visible", &Value::Bool(true)).unwrap();
assert_eq!(doc.to_string(), "menu:\n visible: true\n");
// An existing leaf is an ordinary upsert.
let mut doc = parse_document("menu:\n visible: false\n").unwrap();
doc.set_path("menu.visible", &Value::Bool(true)).unwrap();
assert_eq!(doc.to_string(), "menu:\n visible: true\n");Sourcepub fn remove(&mut self, path: &str) -> Result<()>
pub fn remove(&mut self, path: &str) -> Result<()>
Remove the value at path along with its surrounding entry
(key + colon for mappings, - indicator for sequences).
Trailing whitespace and the line break are removed too so the
surrounding entries close up with no orphan blank line.
§What counts as part of the entry
An entry owns the trivia a reader would say belongs to it, so a removal leaves no orphan and steals nothing from its neighbours:
- Head comment. A contiguous run of full-line comments directly above the entry, at its own indentation, is removed with it. Left behind, such a comment does not merely litter — it silently becomes documentation for the next entry. A blank line detaches the run, so a document header set off by one survives the removal of the first entry.
- Kept blank lines. A keep-chomped (
|+/>+) block scalar’s trailing blank lines are content, not separation, and go with the entry rather than being stranded after it. - Trailing comments stay. A comment after the entry’s last
content line lies outside its value span (see
Document::span_at) and conventionally documents whatever comes next, so it is left in place. A comment interleaved inside a multi-line value is inside the span and goes with the entry.
use noyalib::cst::parse_document;
// The comment documenting `database` goes with it …
let mut doc = parse_document("# connection settings\ndatabase:\n host: x\ncache: 1\n").unwrap();
doc.remove("database").unwrap();
assert_eq!(doc.to_string(), "cache: 1\n");
// … but one that documents the following entry does not.
let mut doc = parse_document("outer:\n a: 1\n # note for next\nnext: 2\n").unwrap();
doc.remove("outer").unwrap();
assert_eq!(doc.to_string(), " # note for next\nnext: 2\n");Coverage (issue #221 sub-ask 4 is complete as of v0.0.23):
- Multi-line values and nested block collections are removed
— the whole entry, from its key /
-indicator through the last line the value owns. - Flow-collection members are removed (
{x: 1, y: 2}→{y: 2},[1, 2, 3]→[1, 3]). The member’s own span goes, plus exactly one separator: the comma after it, or — for the last member — the comma before it. A separator sitting on another line is not matched, so a multi-line flow collection refuses rather than splicing something it cannot see. - The last entry of a collection empties that collection
rather than deleting its bytes:
a:\n x: 1becomesa:\n {}, and a sole sequence item leaves[]. Deleting the bytes would leave a danglinga:, which re-parses as null — a type change rather than a removal. The document’s trailing newline survives.
Every path except the single-line block fast path is guarded by an eager re-parse and a typed-value oracle (the document minus exactly this one path); a splice that would change anything else rolls back and the document is left untouched. The fast path is kept only where the entry demonstrably owns its whole line, because the oracle’s expectation is wrong for a duplicated key.
§Errors
- Path not found.
- The entry sits inside an anchored value with live alias
references — removing it here would remove it at every
*namesite too (#338); callmaterialise_aliases_offirst. - A flow separator that cannot be located on the member’s line.
- The same parse-after-edit errors as
Document::replace_span; on failure the document is left unchanged.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("a: 1\nb: 2\nc: 3\n").unwrap();
doc.remove("b").unwrap();
assert_eq!(doc.to_string(), "a: 1\nc: 3\n");Sourcepub fn rename_key(&mut self, path: &str, new_key: &str) -> Result<()>
pub fn rename_key(&mut self, path: &str, new_key: &str) -> Result<()>
Rename the key of the mapping entry at path to new_key,
leaving every other byte — the :, the value, whitespace,
comments, and sibling entries — untouched.
path addresses the entry the same way Document::set and
Document::remove address it: the path points at the
entry’s value; the operation rewrites that entry’s key
token.
new_key’s spelling is style-matched to the key it
replaces: a plain key stays plain when new_key’s plain
spelling re-parses to exactly that string, a single-quoted
key stays single-quoted, a double-quoted key stays
double-quoted. Quoting is forced only when the plain
spelling would not re-parse to new_key (a: b, -flag,
8080, true) — a plain site then falls back to double
quotes.
Renaming a key to its current spelling is a no-op — Ok(())
with no bytes modified. “Current spelling” is decided on the
decoded key, so a plain true: renamed to "true" stays
plain rather than being requoted. The guarantee applies to
every path that resolves to a mapping entry; paths that fail
to resolve at all (alias-addressed content, keys produced by
a << merge) report their resolution error instead.
After the splice the document must re-parse cleanly and its typed value must equal the old value with exactly that one key renamed — same entry position, same value. If either check fails, the document is rolled back to its previous state and an error is returned.
Restrictions in this phase:
- Both block-mapping and flow-mapping entries rename (#338);
in flow context a new key whose plain spelling would read as
flow structure (
,[]{}) is double-quoted. - The entry’s key must be a simple scalar token (plain,
single-quoted, or double-quoted). Alias keys (
*name :) are rejected. Explicit complex keys (? [a, b]) are not addressable by the path syntax in the first place — their stringified form contains bracket segments, which the path parser reads as sequence indices — so they cannot be renamed; the surrounding mapping’s other entries rename normally.
§Errors
- Path not found, or it does not address a mapping entry (e.g. it ends in a sequence index).
pathcontains a bracket segment that is neither a non-negative integer nor a quoted key (servers[web]) — the shared path parser drops such a segment, which would rename the parent key, sorename_keyrefuses it outright. A key the grammar would misread is addressed asservers["web"].new_keyis<<: the loader treats a<<key as a merge directive whatever its quote style, so the rename cannot round-trip.new_keycontains a non-printable character (any control character other than tab,U+007F, or aU+0080..=U+009FC1 control) — YAML’s printable set excludes them and no scalar style can spell them here.- Restrictions above.
- The containing mapping already has a different entry
whose key equals
new_key— the rename would create a duplicate and silently change data. Reported separately when that sibling comes from a<<merge rather than from the mapping’s own source entries. - The addressed key has no entry of its own because a
<<merge key produced it — the key lives in the merged mapping, so that is where it must be renamed. - The path is reached through an alias (
*name): the bytes at that site belong to the anchor, so the anchor’s own entry must be renamed instead. - The entry lies inside an anchored value that has alias
references — the rename would propagate to every
*namesite. CallDocument::materialise_aliases_offirst. - The re-parse / integrity guard above; the document is left unchanged.
- The document no longer parses (an earlier edit left it in
the optimistically-committed broken state — see
Document::validate).
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("name: foo # the project\nversion: 0.0.1\n").unwrap();
doc.rename_key("name", "title").unwrap();
assert_eq!(doc.to_string(), "title: foo # the project\nversion: 0.0.1\n");A new key that is not plain-safe is quoted automatically:
use noyalib::cst::parse_document;
let mut doc = parse_document("name: foo\n").unwrap();
doc.rename_key("name", "a: b").unwrap();
assert_eq!(doc.to_string(), "\"a: b\": foo\n");
assert_eq!(doc.as_value()["a: b"].as_str(), Some("foo"));Sourcepub fn swap_items(&mut self, path: &str, i: usize, j: usize) -> Result<()>
pub fn swap_items(&mut self, path: &str, i: usize, j: usize) -> Result<()>
Swap two items of the block sequence at path, exchanging each
item’s whole entry — its own lines, its head-comment run
included. Every other item, and the surrounding structure, stay
byte-identical.
An item owns the same range here that remove
deletes — owned_entry_range computes both. That is deliberate:
the two
have to agree about who a comment belongs to, or the same bytes
are the entry’s property under one call and the slot’s under the
other. A reorder that moved only value bytes would leave each
comment annotating whichever item landed beneath it, silently
and at Ok.
A flow sequence has no per-item lines to exchange, so its members keep the narrower value-span swap.
Guarded like the other mutators: after the two splices the
document must re-parse and its typed value must equal the
original with exactly items i and j exchanged, or the edit
is rolled back and the document is left untouched.
Swapping an index with itself, or two items whose values are
already equal, is a no-op that returns Ok(()).
§Errors
pathdoes not resolve to a sequence.iorjis out of bounds for that sequence.- The bytes of an item could not be located.
- The splice would not re-parse, or fails the integrity check above (both roll back).
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("- a\n- b\n- c\n").unwrap();
doc.swap_items("", 0, 2).unwrap();
assert_eq!(doc.source(), "- c\n- b\n- a\n");A comment travels with the item it documents:
use noyalib::cst::parse_document;
let mut doc = parse_document("# about one\n- one\n# about two\n- two\n").unwrap();
doc.swap_items("", 0, 1).unwrap();
assert_eq!(doc.source(), "# about two\n- two\n# about one\n- one\n");Sourcepub fn move_item(&mut self, path: &str, from: usize, to: usize) -> Result<()>
pub fn move_item(&mut self, path: &str, from: usize, to: usize) -> Result<()>
Move the item at from to index to in the block sequence at
path, shifting the items in between by one. The move is
applied as a run of adjacent swap_items
steps, so it inherits that method’s guarantees — each item’s
whole entry moves, its comments with it, structure is preserved,
and each step is guarded — and the whole move is atomic: if
any step is refused, the document is rolled back to its state
before the call.
Moving an index to itself is a no-op that returns Ok(()).
§Errors
pathdoes not resolve to a sequence.fromortois out of bounds for that sequence.- Any underlying swap is refused; the document is left unchanged.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("- a\n- b\n- c\n- d\n").unwrap();
doc.move_item("", 0, 2).unwrap();
assert_eq!(doc.source(), "- b\n- c\n- a\n- d\n");Sourcepub fn push_back(&mut self, path: &str, fragment: &str) -> Result<()>
pub fn push_back(&mut self, path: &str, fragment: &str) -> Result<()>
Append a new item to the block sequence at path.
fragment is the YAML representation of the value — the
- indicator and the surrounding indentation are synthesized
from the existing items so the new line matches the file’s
shape. Block sequences only in this phase; flow sequences
([…]) and empty sequences are rejected.
§Errors
pathdoes not resolve to a sequence.- The sequence is a flow collection (
[…]). - The sequence has no existing items to anchor indentation on.
- The fragment changed the document beyond the single item
asked for — reaching outside the sequence (
"v\nqq: 7") or smuggling extra items into it ("v\n - w"); the document is left unchanged. - The same parse-after-edit errors as
Document::replace_span.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("items:\n - one\n - two\n").unwrap();
doc.push_back("items", "three").unwrap();
assert_eq!(doc.to_string(), "items:\n - one\n - two\n - three\n");Sourcepub fn indent_unit(&self) -> usize
pub fn indent_unit(&self) -> usize
Detect the indentation unit (in spaces) used by this document.
Walks the source line-by-line, looks for any pair of
consecutive non-empty/non-comment lines where the second is
more deeply indented than the first, and returns the smallest
such delta — that is the file’s “indent step”, typically 2 or
4 spaces. A document with no nested structure (or only
top-level keys) has no detectable step; the default 2 is
returned in that case.
Used internally by the crate::cst::Entry insertion paths
to keep the inserted YAML’s inner indentation consistent with
what the rest of the file already uses (2-space file → 2-space
inserts; 4-space file → 4-space inserts). Exposed publicly so
callers building their own emission paths can match the same
convention.
§Examples
use noyalib::cst::parse_document;
let two_space = parse_document(
"metadata:\n labels:\n app: noyalib\n",
).unwrap();
assert_eq!(two_space.indent_unit(), 2);
let four_space = parse_document(
"metadata:\n labels:\n app: noyalib\n",
).unwrap();
assert_eq!(four_space.indent_unit(), 4);
// No nested structure — defaults to 2.
let flat = parse_document("a: 1\nb: 2\n").unwrap();
assert_eq!(flat.indent_unit(), 2);Sourcepub fn dominant_quote_style(&self) -> ScalarStyle
pub fn dominant_quote_style(&self) -> ScalarStyle
Inspect the document and return the dominant scalar quote
style — Plain, SingleQuoted, or DoubleQuoted. Used by
the crate::cst::Entry insert helpers to make new
scalars adopt the file’s existing convention rather than
the serializer’s hard-coded default.
The detection scans every plain / single-quoted /
double-quoted scalar leaf in the green tree, picks the
majority, and breaks ties in favour of the simpler form
(Plain > SingleQuoted > DoubleQuoted). Empty
documents and documents with no string-shaped scalars
default to Plain.
§Examples
use noyalib::cst::parse_document;
use noyalib::ScalarStyle;
let single = parse_document("a: 'one'\nb: 'two'\n").unwrap();
assert_eq!(single.dominant_quote_style(), ScalarStyle::SingleQuoted);
let double = parse_document("a: \"one\"\nb: \"two\"\n").unwrap();
assert_eq!(double.dominant_quote_style(), ScalarStyle::DoubleQuoted);
let plain = parse_document("a: one\nb: two\n").unwrap();
assert_eq!(plain.dominant_quote_style(), ScalarStyle::Plain);Sourcepub fn dominant_flow_style(&self) -> FlowStyle
pub fn dominant_flow_style(&self) -> FlowStyle
Inspect the document and return the dominant collection
style — FlowStyle::Block or FlowStyle::Auto
(equivalent to “flow”). Used by Entry::insert_value to
decide whether a typed mapping / sequence emission should
use block or flow form.
The detection counts top-level BlockMapping /
BlockSequence vs FlowMapping / FlowSequence leaves
and picks the majority. Empty / scalar-only documents
default to Block.
§Examples
use noyalib::cst::parse_document;
use noyalib::FlowStyle;
let block = parse_document("a:\n - 1\n - 2\n").unwrap();
assert_eq!(block.dominant_flow_style(), FlowStyle::Block);
let flow = parse_document("a: [1, 2, 3]\nb: [4, 5]\n").unwrap();
assert_eq!(flow.dominant_flow_style(), FlowStyle::Auto);Sourcepub fn insert_entry(
&mut self,
mapping_path: &str,
key: &str,
fragment: &str,
) -> Result<()>
pub fn insert_entry( &mut self, mapping_path: &str, key: &str, fragment: &str, ) -> Result<()>
Insert a new key: fragment entry into the block mapping at
mapping_path. The mapping-side analogue of
Document::push_back.
Behaves like set when the key already exists (the value is
replaced losslessly). When the key is new, a sibling line is
spliced after the last existing entry, with the indent matched
to the last entry’s key column so the file stays canonical.
Block mappings only in this phase; flow mappings ({…}) and
empty mappings are rejected.
Only the fragment is verbatim YAML. The key is a name: a
spelling whose plain form would not re-parse to it (a: b,
[x], a leading - ) is quoted automatically, exactly as
Document::rename_key documents for its new key, and the
existing-key check reads the mapping’s own entries — a key
holding . or [ (app.io/name, ubiquitous in Kubernetes
labels) inserts as that literal key rather than resolving
through the path syntax.
§The fragment cannot reach outside the entry
After the splice, the document’s shape outside mapping_path
must be unchanged and the mapping must have gained exactly the
one entry asked for — a fragment or key that smuggles sibling
entries (a line break the splice never intended, v\rc: 3) is
refused and the document left untouched, the same oracle
Document::set and Document::push_back apply.
§Errors
mapping_pathdoes not resolve to a mapping.- The mapping is empty (no anchor for indentation; use
setwith a fragment instead). keyis<<(the loader reads any<<key as a merge directive, whatever its quote style) or carries a non-printable character.keyalready exists but contains.or[, which the path syntax cannot address to replace its value —removethe entry and insert it afresh, or splice it withset.- The fragment added or removed entries beyond the one asked for (the integrity oracle above); the document is left unchanged.
- The same parse-after-edit errors as
Document::replace_span.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document(
"metadata:\n labels:\n app: noyalib\n",
).unwrap();
doc.insert_entry("metadata.labels", "env", "prod").unwrap();
let out = doc.to_string();
assert!(out.contains("app: noyalib"));
assert!(out.contains("env: prod"));Sourcepub fn insert_after(&mut self, item_path: &str, fragment: &str) -> Result<()>
pub fn insert_after(&mut self, item_path: &str, fragment: &str) -> Result<()>
Insert a new sequence item immediately after the item at
item_path (e.g. "items[1]").
fragment is the YAML representation of the value; the
- indicator and indentation are derived from the item at
item_path.
§Errors
item_pathdoes not end in an index.- The path does not resolve to a sequence item in a block sequence.
- The fragment changed the document beyond the single item
asked for — the same containment oracle
Document::push_backdocuments; the document is left unchanged. - The same parse-after-edit errors as
Document::replace_span.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("items:\n - one\n - three\n").unwrap();
doc.insert_after("items[0]", "two").unwrap();
assert_eq!(
doc.to_string(),
"items:\n - one\n - two\n - three\n",
);Sourcepub fn insert_entry_value<E: Emit + ?Sized>(
&mut self,
mapping_path: &str,
key: &str,
value: &E,
) -> Result<()>
pub fn insert_entry_value<E: Emit + ?Sized>( &mut self, mapping_path: &str, key: &str, value: &E, ) -> Result<()>
Insert key: value into the block mapping at mapping_path,
formatting both halves so they re-parse to exactly the key
and value given.
The typed counterpart of Document::insert_entry, which
splices its &str arguments verbatim: insert_entry(m, "k", "a: b") grows a nested mapping, where
insert_entry_value(m, "k", "a: b") inserts the string
"a: b". Quoting follows the file’s dominant scalar style
except where that style would misrepresent the data, in which
case quoting is forced (see Emit).
When key already exists its value is replaced in place;
otherwise a sibling line is appended after the mapping’s last
entry, indented to match.
After the splice the document must re-parse and its typed value must equal the pre-edit value with exactly this one entry set, or the edit is rolled back — the guard the verbatim path cannot offer, since a fragment that restructures the document is still valid YAML.
An existing key is an upsert: its value is rewritten in place,
including when that value is an implicit null (a:), which is an entry
the mapping already has rather than one to append. A key it only
inherits through a << merge has no entry here at all, so an
explicit one is created to override it.
A new key into a flow mapping — {a: 1}, {}, or a whole
document spelled as one — splices , key: value before the
closing brace (#338); only single-line flow mappings accept
inserts.
§Errors
mapping_pathdoes not resolve to a mapping, or an empty block-context mapping leaves no entry to anchor indentation on (useDocument::setwith a fragment).- The flow mapping at
mapping_pathspans more than one line. keyis<<(the loader reads any<<key as a merge directive, whatever its quote style) or carries a non-printable character.- The value has no auto-formatted spelling (see
Emit::emit). - The splice would not re-parse, or fails the integrity check above; the document is left unchanged.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("labels:\n app: noyalib\n").unwrap();
doc.insert_entry_value("labels", "version", "8080").unwrap();
// Quoted: the plain spelling would load as a number.
assert_eq!(
doc.to_string(),
"labels:\n app: noyalib\n version: \"8080\"\n",
);Sourcepub fn push_back_value<E: Emit + ?Sized>(
&mut self,
path: &str,
value: &E,
) -> Result<()>
pub fn push_back_value<E: Emit + ?Sized>( &mut self, path: &str, value: &E, ) -> Result<()>
Append value to the block sequence at path, formatted so it
re-parses to exactly that value.
The typed counterpart of Document::push_back, which splices
its &str verbatim: push_back("items", "- x") grows a nested
sequence, where push_back_value("items", "- x") appends the
string "- x". Guarded by the same re-parse plus typed-value
oracle as Document::insert_entry_value.
A flow sequence takes , value before its closing bracket
instead of a new - line, and [] receives its first member
(#338); only single-line flow collections accept inserts.
§Errors
pathdoes not resolve to a sequence, or an empty block sequence leaves no item to anchor indentation on.- The flow sequence at
pathspans more than one line. - The value has no auto-formatted spelling (see
Emit::emit). - The splice would not re-parse, or fails the integrity check; the document is left unchanged.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("items:\n - one\n").unwrap();
doc.push_back_value("items", "two: 2").unwrap();
assert_eq!(doc.to_string(), "items:\n - one\n - \"two: 2\"\n");Sourcepub fn insert_after_value<E: Emit + ?Sized>(
&mut self,
item_path: &str,
value: &E,
) -> Result<()>
pub fn insert_after_value<E: Emit + ?Sized>( &mut self, item_path: &str, value: &E, ) -> Result<()>
Insert value immediately after the sequence item at
item_path (e.g. "items[1]"), formatted so it re-parses to
exactly that value.
The typed counterpart of Document::insert_after, guarded by
the same re-parse plus typed-value oracle as
Document::insert_entry_value.
Inside a single-line flow sequence the new member follows
the addressed item’s own span: [a, b] after item 0 becomes
[a, v, b] (#338).
§Errors
item_pathdoes not end in an index, or does not resolve to a sequence item.- The flow sequence spans more than one line.
- The value has no auto-formatted spelling (see
Emit::emit). - The splice would not re-parse, or fails the integrity check; the document is left unchanged.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document("items:\n - one\n - three\n").unwrap();
doc.insert_after_value("items[0]", "two").unwrap();
assert_eq!(doc.to_string(), "items:\n - one\n - two\n - three\n");Source§impl Document
impl Document
Sourcepub fn entry<'a>(&'a mut self, path: &str) -> Entry<'a>
pub fn entry<'a>(&'a mut self, path: &str) -> Entry<'a>
Return a path-shaped mutable handle to the node at path.
The handle is the “pro” mutation interface — chainable,
composable, ergonomic for nested edits — that complements the
functional Document::set / Document::remove /
Document::push_back / Document::insert_after methods
(all of which remain available for direct one-shot operations).
entry itself is infallible — the path is recorded but not
resolved at this point. Operations on the returned entry
surface path-resolution and splice-failure errors via their
own Result.
§Examples
use noyalib::cst::parse_document;
let mut doc = parse_document(
"metadata:\n labels:\n app: noyalib\n",
).unwrap();
doc.entry("metadata.labels").insert("env", "prod").unwrap();
let out = doc.to_string();
assert!(out.contains("app: noyalib"));
assert!(out.contains("env: prod"));Trait Implementations§
Auto Trait Implementations§
impl !Freeze for Document
impl !RefUnwindSafe for Document
impl !Sync for Document
impl !UnwindSafe for Document
impl Send for Document
impl Unpin for Document
impl UnsafeUnpin for Document
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> ErasedDestructor for Twhere
T: 'static,
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreimpl<T> MaybeSendSync for T
Source§impl<D> OwoColorize for D
impl<D> OwoColorize for D
Source§fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>where
C: Color,
Source§fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>where
C: Color,
Source§fn black(&self) -> FgColorDisplay<'_, Black, Self>
fn black(&self) -> FgColorDisplay<'_, Black, Self>
Source§fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
fn on_black(&self) -> BgColorDisplay<'_, Black, Self>
Source§fn red(&self) -> FgColorDisplay<'_, Red, Self>
fn red(&self) -> FgColorDisplay<'_, Red, Self>
Source§fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
fn on_red(&self) -> BgColorDisplay<'_, Red, Self>
Source§fn green(&self) -> FgColorDisplay<'_, Green, Self>
fn green(&self) -> FgColorDisplay<'_, Green, Self>
Source§fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
fn on_green(&self) -> BgColorDisplay<'_, Green, Self>
Source§fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>
Source§fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>
Source§fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
fn blue(&self) -> FgColorDisplay<'_, Blue, Self>
Source§fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>
Source§fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>
Source§fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>
Source§fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>
Source§fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>
Source§fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>
Source§fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>
Source§fn white(&self) -> FgColorDisplay<'_, White, Self>
fn white(&self) -> FgColorDisplay<'_, White, Self>
Source§fn on_white(&self) -> BgColorDisplay<'_, White, Self>
fn on_white(&self) -> BgColorDisplay<'_, White, Self>
Source§fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
fn default_color(&self) -> FgColorDisplay<'_, Default, Self>
Source§fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>
Source§fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>
Source§fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>
Source§fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>
Source§fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>
Source§fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>
Source§fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>
Source§fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>
Source§fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>
Source§fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>
Source§fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>
Source§fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
Source§fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
Source§fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>
Source§fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>
Source§fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>
Source§fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>
Source§fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>
Source§fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>
Source§fn bold(&self) -> BoldDisplay<'_, Self>
fn bold(&self) -> BoldDisplay<'_, Self>
Source§fn dimmed(&self) -> DimDisplay<'_, Self>
fn dimmed(&self) -> DimDisplay<'_, Self>
Source§fn italic(&self) -> ItalicDisplay<'_, Self>
fn italic(&self) -> ItalicDisplay<'_, Self>
Source§fn underline(&self) -> UnderlineDisplay<'_, Self>
fn underline(&self) -> UnderlineDisplay<'_, Self>
Source§fn blink(&self) -> BlinkDisplay<'_, Self>
fn blink(&self) -> BlinkDisplay<'_, Self>
Source§fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
fn blink_fast(&self) -> BlinkFastDisplay<'_, Self>
Source§fn reversed(&self) -> ReversedDisplay<'_, Self>
fn reversed(&self) -> ReversedDisplay<'_, Self>
Source§fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>
Source§fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::fg or
a color-specific method, such as OwoColorize::green, Read moreSource§fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>where
Color: DynColor,
OwoColorize::bg or
a color-specific method, such as OwoColorize::on_yellow, Read moreSource§fn fg_rgb<const R: u8, const G: u8, const B: u8>(
&self,
) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>
fn fg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>
Source§fn bg_rgb<const R: u8, const G: u8, const B: u8>(
&self,
) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>
fn bg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>
Source§fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>
fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>
Source§fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>
fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>
Source§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
Source§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();Source§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
Source§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
Source§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
Source§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
Source§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
Source§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
Source§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
Source§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
Source§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();Source§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
Source§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
Source§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
Source§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
Source§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
Source§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
Source§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
Source§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
Source§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
Source§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
Source§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling Attribute value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();Source§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
Source§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi Quirk value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();Source§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
Source§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the Condition value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);Source§impl<T> Pointable for T
impl<T> Pointable for T
Source§impl<T> ToCompactString for Twhere
T: Display,
impl<T> ToCompactString for Twhere
T: Display,
Source§fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
fn try_to_compact_string(&self) -> Result<CompactString, ToCompactStringError>
ToCompactString::to_compact_string() Read moreSource§fn to_compact_string(&self) -> CompactString
fn to_compact_string(&self) -> CompactString
CompactString. Read more