pub struct Document { /* private fields */ }Expand description
A fully-parsed, typed in-memory Quillmark document.
Document is the canonical representation of a Quillmark Markdown file.
Markdown is both the import and export format; the structured data here
is primary.
§Structure
main— the entryCard(sentinel isSentinel::Main(reference)).cards— ordered composable cards (each withSentinel::Card(tag)).
Backend plates consume the flat JSON wire shape produced by
Document::to_plate_json. That method is the only place in core
that reconstructs {"QUILL": ..., "CARDS": [...], "BODY": "..."}.
Implementations§
Source§impl Document
impl Document
Sourcepub fn set_quill_ref(&mut self, reference: QuillReference)
pub fn set_quill_ref(&mut self, reference: QuillReference)
Sourcepub fn card_mut(&mut self, index: usize) -> Option<&mut Card>
pub fn card_mut(&mut self, index: usize) -> Option<&mut Card>
Return a mutable reference to the composable card at index, or None
if out of range.
§Warnings
This method never modifies warnings.
Sourcepub fn push_card(&mut self, card: Card)
pub fn push_card(&mut self, card: Card)
Append a composable card to the end of the card list.
§Invariants
card.sentinel() must be Sentinel::Card; a main card cannot be
appended as a composable card. Debug assert.
§Warnings
This method never modifies warnings.
Sourcepub fn insert_card(&mut self, index: usize, card: Card) -> Result<(), EditError>
pub fn insert_card(&mut self, index: usize, card: Card) -> Result<(), EditError>
Insert a composable card at index.
§Invariants enforced
index must be in 0..=len. An index > len returns
EditError::IndexOutOfRange.
§Warnings
This method never modifies warnings.
Sourcepub fn remove_card(&mut self, index: usize) -> Option<Card>
pub fn remove_card(&mut self, index: usize) -> Option<Card>
Remove and return the composable card at index, or None if out of range.
§Warnings
This method never modifies warnings.
Sourcepub fn set_card_tag(
&mut self,
index: usize,
new_tag: impl Into<String>,
) -> Result<(), EditError>
pub fn set_card_tag( &mut self, index: usize, new_tag: impl Into<String>, ) -> Result<(), EditError>
Replace the tag (sentinel) of the composable card at index.
Field-bag semantics. This mutates only the sentinel; the card’s frontmatter and body are untouched. After the call:
- Fields valid under both old and new schemas round-trip unchanged.
- Fields only in the old schema linger in the bag (silently ignored
by
Quill::formandvalidate_document, but still emitted byto_markdown()). - Fields only in the new schema are absent — surfaced as
DefaultorMissingbyQuill::form, andMissingRequiredbyvalidate_document.
Schema-aware migration (clearing orphans, applying defaults, etc.) is
the caller’s responsibility — set_card_tag is a structural primitive.
§Invariants enforced
indexmust be in0..len. Out of range returnsEditError::IndexOutOfRange.new_tagmust match[a-z_][a-z0-9_]*. Invalid tags returnEditError::InvalidTagName.
§Warnings
This method never modifies warnings.
Sourcepub fn move_card(&mut self, from: usize, to: usize) -> Result<(), EditError>
pub fn move_card(&mut self, from: usize, to: usize) -> Result<(), EditError>
Move the composable card at from to position to.
If from == to, this is a no-op and returns Ok(()).
§Invariants enforced
Both from and to must be in 0..len. Either being out of range
returns EditError::IndexOutOfRange with the offending index.
§Warnings
This method never modifies warnings.
Source§impl Document
impl Document
Sourcepub fn to_markdown(&self) -> String
pub fn to_markdown(&self) -> String
Emit canonical Quillmark Markdown from this document.
§Contract
-
Type-fidelity round-trip.
Document::from_markdown(&doc.to_markdown())returns aDocumentequal todocby value and by type variant.QuillValue::String("on")round-trips as a string, never as a bool.QuillValue::String("01234")round-trips as a string, never as an integer. This guarantee is the whole point of owning emission. -
Emit-idempotent.
to_markdownis a pure function ofdoc; two calls on the samedocreturn byte-equal strings.
Byte-equality with the original source is not guaranteed.
§Emission rules (§5.2)
- Line endings:
\nonly. CRLF normalization happens on import. - Frontmatter:
---\n,QUILL: <ref>first, remaining fields inIndexMapinsertion order,---\n, blank line. - Cards: one blank line before each, fence
---\nCARD: <tag>\n<fields>\n---\n<body>. - Body: emitted verbatim after frontmatter (and cards).
- Mappings and sequences: block style at every nesting level.
- Booleans:
true/false. - Null:
null. - Numbers: bare literals (integer or float as stored in
serde_json::Value). - Strings: always double-quoted, JSON-style escaping
(
\",\\,\n,\t,\uXXXXfor control chars). This is the load-bearing rule that guarantees type fidelity. - Multi-line strings: double-quoted with
\nescape sequences. No block scalars (|,>) in v1.
§Open decisions (resolved)
-
Nested-map order.
QuillValueis backed byserde_json::Valuewhose object type (serde_json::Map) preserves insertion order when theserde_json/preserve_orderfeature is enabled (it is in this workspace). Insertion order is therefore preserved for nested maps at emit time. -
Empty containers.
- Empty object (
{}) → the key is omitted from emit entirely. - Empty array (
[]) → emitted askey: []\n.
- Empty object (
§What is lost
- YAML comments: stripped during parsing; not stored in
Document. - Custom tags (
!fill): the tag is dropped; the scalar value is preserved. On re-emit the tag does not appear. - Original quoting style: all strings are re-emitted double-quoted regardless of how they were written in the source.
Source§impl Document
impl Document
Sourcepub fn from_main_and_cards(
main: Card,
cards: Vec<Card>,
warnings: Vec<Diagnostic>,
) -> Self
pub fn from_main_and_cards( main: Card, cards: Vec<Card>, warnings: Vec<Diagnostic>, ) -> Self
Create a Document from a pre-built main Card and composable cards.
The caller must guarantee that main.sentinel is Sentinel::Main(_)
and every card in cards has sentinel = Sentinel::Card(_).
Sourcepub fn from_markdown(markdown: &str) -> Result<Self, ParseError>
pub fn from_markdown(markdown: &str) -> Result<Self, ParseError>
Parse a Quillmark Markdown document, discarding any non-fatal warnings.
Sourcepub fn from_markdown_with_warnings(
markdown: &str,
) -> Result<ParseOutput, ParseError>
pub fn from_markdown_with_warnings( markdown: &str, ) -> Result<ParseOutput, ParseError>
Parse a Quillmark Markdown document, returning warnings alongside the document.
Sourcepub fn quill_reference(&self) -> &QuillReference
pub fn quill_reference(&self) -> &QuillReference
The quill reference (name@version-selector) carried by the main card’s
sentinel. Convenience reader over doc.main().sentinel().
Sourcepub fn warnings(&self) -> &[Diagnostic]
pub fn warnings(&self) -> &[Diagnostic]
Non-fatal warnings collected during parsing.
Sourcepub fn to_plate_json(&self) -> Value
pub fn to_plate_json(&self) -> Value
Serialize this document to the JSON shape expected by backend plates.
The output has the following top-level keys, which match what
lib.typ.template reads at Typst runtime:
{
"QUILL": "<ref>",
"<field>": <value>,
...
"BODY": "<global-body>",
"CARDS": [
{ "CARD": "<tag>", "<field>": <value>, ..., "BODY": "<card-body>" },
...
]
}This is the only place in quillmark-core that knows about the plate
wire format. All internal consumers (Quill, backends) call this instead
of constructing the shape by hand.