Skip to main content

Document

Struct Document 

Source
pub struct Document { /* private fields */ }
Expand description

A fully-parsed Quillmark document. Serde routes through StoredDocument; for the plate wire shape see Document::to_plate_json.

Parse-time warnings are not document state — they ride out-of-band on Parsed from Document::parse, the single owner. Equality and the storage DTO therefore cover only structural content (main and cards).

Implementations§

Source§

impl Document

Source

pub fn set_quill_ref(&mut self, reference: QuillReference)

Source

pub fn card_mut(&mut self, index: usize) -> Option<&mut Card>

Source

pub fn push_card(&mut self, card: Card) -> Result<(), EditError>

Append a composable card. Its $kind must be a valid, non-reserved composable kind (EditError::InvalidKindName / EditError::ReservedKind otherwise) — the invariant for any card in the cards list, enforced here so every entry path shares it.

Source

pub fn insert_card(&mut self, index: usize, card: Card) -> Result<(), EditError>

Insert a composable card at index (index > lenEditError::IndexOutOfRange; invalid $kindEditError::InvalidKindName / EditError::ReservedKind).

Source

pub fn remove_card(&mut self, index: usize) -> Option<Card>

Source

pub fn set_card_kind( &mut self, index: usize, new_kind: impl Into<String>, ) -> Result<(), EditError>

Replace the $kind of the composable card at index.

Only the $kind metadata changes; the payload and body are untouched (field-bag semantics). Old-schema fields linger in the bag; new-schema fields are absent until set explicitly. Schema migration is the caller’s responsibility — this is a structural primitive.

Returns EditError::IndexOutOfRange, EditError::InvalidKindName, or EditError::ReservedKind on constraint violations.

Source

pub fn move_card(&mut self, from: usize, to: usize) -> Result<(), EditError>

Move card at from to position to. No-op when from == to. Either index out of range → EditError::IndexOutOfRange.

Source§

impl Document

Source

pub fn to_markdown(&self) -> String

Emit canonical Quillmark Markdown from this document.

§Contract
  1. Type-fidelity round-trip. Document::parse(&doc.to_markdown()) returns a Document equal to doc by 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.

    Content-field carve-out. A richtext field committed as a canonical content object (and the card $body) is intentionally markdown-lossy on markdown emit: it projects to its markdown form (project_content_field), so identity marks (anchors, island ids) and content-only marks (underline) do not survive a to_markdownfrom_markdown round-trip. On-disk identity is markdown-lossy by design; the storage DTO is the lossless carrier. The value-equality guarantee above holds for every field the writer did not commit as canonical content.

  2. Emit-idempotent. to_markdown is a pure function of doc; two calls on the same doc return byte-equal strings.

Byte-equality with the original source is not guaranteed.

§Emission rules (§9)
  • Line endings: \n only. CRLF normalization happens on import.
  • Every block is emitted as a ~~~ card-yaml fence: a bare ~~~ opener, the $-prefixed system-metadata lines ($quill: <ref> for the root block, $kind: <kind> for composable cards) leading the YAML payload, the user-defined data fields, then a closing ~~~.
  • Cards: one blank line before each, then the block, then the card body.
  • Body: emitted verbatim after the root block (and after each card).
  • Mappings and sequences: block style at every nesting level.
  • Scalars (booleans, null, numbers, strings): delegated to serde-saphyr, which emits the type-canonical form (true/ false, null, bare numeric literal) and quotes strings only when the unquoted form would be misread (on/yes/off, null/~, numeric-looking strings, leading flow indicators, : runs, …). Quoting form is not stable — what matters is that the emitted scalar round-trips to the same QuillValue variant. This is the type-fidelity guarantee.
  • Multi-line strings: emitted as inline double-quoted scalars with \n escapes; no | / > block forms.
§Design notes
  • Nested-map order. QuillValue is backed by serde_json::Value whose object type (serde_json::Map) preserves insertion order when the serde_json/preserve_order feature 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 as key: []\n.
§What is preserved
  • YAML comments: own-line and inline trailing comments round-trip at their source position. Comments whose host disappears at emit time (empty-mapping omission, programmatic field removal) degrade to own-line comments at the same indent so the comment text is preserved even when its position shifts.
  • !must_fill tags: round-trip via the fill flag on PayloadItem::Field.
§What is lost
  • Other custom tags (!include, !env, …): the tag is dropped; the scalar value is preserved.
  • Original quoting style: strings are re-emitted in saphyr’s canonical form (plain when safe, quoted when ambiguous). The form chosen for emit may not match the form in the source.
Source§

impl Document

Source

pub fn new(quill: QuillReference) -> Self

Create a blank document: a main card carrying only $quill, an empty body, and no composable cards. The programmatic blank canvas — every schema field is absent and resolves at render time (default, else type-empty zero), so nothing the caller did not set reaches the output. For an example-filled starter shaped like the blueprint, use Quill::seed_document.

Source

pub fn from_main_and_cards(main: Card, cards: Vec<Card>) -> Self

Create a Document from a pre-built main card and composable cards. main must carry $quill; composable cards must not.

Source

pub fn parse(markdown: &str) -> Result<Parsed, ParseError>

Parse card-yaml Markdown into a Parsed — the Document plus any non-fatal warnings. The single parse entry; a caller that wants only the document writes Document::parse(md)?.document. Errors on malformed YAML, a missing root $quill, an over-size input, and the other ParseError variants.

Source

pub fn main(&self) -> &Card

Source

pub fn main_mut(&mut self) -> &mut Card

Source

pub fn quill_reference(&self) -> QuillReference

The $quill reference from the root block. Always present on parsed documents.

Source

pub fn cards(&self) -> &[Card]

Source

pub fn cards_mut(&mut self) -> &mut [Card]

Source

pub fn card(&self, index: usize) -> Option<&Card>

A single composable card by index — the immutable twin of card_mut, so reading one card’s payload does not require materializing every card via cards. None when out of range.

Source

pub fn find_card(&self, id: &str) -> Option<(usize, &Card)>

The first composable card whose $id equals id, with its index — resolving the canonical durable address (PROGRAMMATIC.md) without a hand-rolled scan over cards. $id is non-unique by design, so this returns the first match; None when no card carries it.

Source

pub fn to_plate_json(&self) -> Value

Serialize to the JSON wire shape consumed by backend plates. This is the only place in quillmark-core that produces this shape:

{
  "$quill": "<ref>",
  "$body": { "text": "…", "lines": [...], "marks": [...], "islands": [...] },
  "$cards": [{ "$kind": "<tag>", "$body": <content>, "<field>": <value>, ... }],
  "<field>": <value>, ...
}

$body (global and per-card) is canonical Content-JSON — the content as a nested object, not a markdown string. Richtext payload fields likewise cross as content objects (committed at coercion time).

$-prefixed keys carry document-level metadata (quill ref, body text, card list, card kind). User payload fields stay flat at the root — they cannot collide with $ keys because user field names are never $-prefixed (they match [A-Za-z_][A-Za-z0-9_]*).

Trait Implementations§

Source§

impl Clone for Document

Source§

fn clone(&self) -> Document

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Document

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Document

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl From<&Document> for DocumentV0_93_0

Source§

fn from(doc: &Document) -> Self

Converts to this type from the input type.
Source§

impl From<Document> for StoredDocument

Source§

fn from(doc: Document) -> Self

Converts to this type from the input type.
Source§

impl PartialEq for Document

Source§

fn eq(&self, other: &Document) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl Serialize for Document

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Document

Source§

impl TryFrom<DocumentV0_93_0> for Document

Source§

type Error = StorageError

The type returned in the event of a conversion error.
Source§

fn try_from(payload: DocumentV0_93_0) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl TryFrom<StoredDocument> for Document

Source§

type Error = StorageError

The type returned in the event of a conversion error.
Source§

fn try_from(stored: StoredDocument) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.