onetaskgraph_plugin_api/section.rs
1//! What a source says about the headings of a record it stores, and the markup a body is
2//! written and read with where its destination spells part of it otherwise than authored.
3//!
4//! Both exist for what a copy writes into a body in each destination's own form: a section
5//! link — a link to one heading of another record, or of the same one — and an expandable
6//! block. How each destination spells either is decided once, in the engine, and never here:
7//! a plugin only reports what its backend stores ([`StoredHeading`]) and applies the markup it
8//! is handed ([`BodyMarkup`]), so it holds no copy of any form.
9
10use std::sync::Arc;
11
12/// One heading of a record, as its backend stores it: its text, and the id the backend gave
13/// it.
14///
15/// Reported only by a backend that assigns its headings ids of its own — Linear's editor
16/// state gives every heading node an `id` attribute — and in the order the headings appear
17/// in the record, so the n-th heading with one text is the n-th entry with that text.
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct StoredHeading {
20 /// The heading's text, without its leading `#` marks or any inline formatting.
21 pub text: String,
22 /// The id its backend stores the heading under.
23 // llmlint: ignore[invalid_states_unrepresentable] The id is whatever a backend stores, and the contract crate states no backend's id grammar; a plugin keeps only well-formed ids (Linear's decoder keeps hex ids carrying 8 hex characters) and the engine's form checks the hex it writes, so a newtype here would only move that check into a crate every plugin rebuilds against.
24 pub id: String,
25}
26
27/// Markup a destination's bodies carry — an anchor a reader addresses a heading by, or a
28/// collapsible section in the destination's own spelling — which the engine hands a source
29/// when it builds it.
30///
31/// A source given one applies [`written`](Self::written) to every body it sends its backend
32/// and [`read`](Self::read) to every body it reads back, so what the engine reads through
33/// the source is the body as it was authored. Defaulted away entirely: a source given none
34/// stores what it is given.
35pub trait BodyMarkup: Send + Sync {
36 /// `content` as the backend is sent it.
37 fn written(&self, content: &str) -> String;
38 /// `content` read from the backend, with exactly the markup [`written`](Self::written)
39 /// adds removed.
40 fn read(&self, content: &str) -> String;
41}
42
43/// A shared [`BodyMarkup`], as a source holds the one it was handed.
44pub type SharedBodyMarkup = Arc<dyn BodyMarkup>;