Skip to main content

acdc_parser/model/inlines/
text.rs

1use serde::{
2    Serialize,
3    ser::{SerializeMap, Serializer},
4};
5
6use crate::{Location, Role, Substitution};
7
8use super::InlineNode;
9
10/// The form of an inline formatting element (how it was expressed in the source)
11#[derive(Clone, Debug, PartialEq, Serialize)]
12#[serde(rename_all = "lowercase")]
13pub enum Form {
14    Constrained,
15    Unconstrained,
16}
17
18/// A `Subscript` represents a subscript section of text in a document.
19#[derive(Clone, Debug, PartialEq)]
20pub struct Subscript<'a> {
21    pub role: Option<Role<'a>>,
22    pub id: Option<&'a str>,
23    pub form: Form,
24    pub content: Vec<InlineNode<'a>>,
25    pub location: Location,
26}
27
28/// A `Superscript` represents a superscript section of text in a document.
29#[derive(Clone, Debug, PartialEq)]
30pub struct Superscript<'a> {
31    pub role: Option<Role<'a>>,
32    pub id: Option<&'a str>,
33    pub form: Form,
34    pub content: Vec<InlineNode<'a>>,
35    pub location: Location,
36}
37
38/// A `CurvedQuotation` represents a curved quotation section of text in a document.
39#[derive(Clone, Debug, PartialEq)]
40pub struct CurvedQuotation<'a> {
41    pub role: Option<Role<'a>>,
42    pub id: Option<&'a str>,
43    pub form: Form,
44    pub content: Vec<InlineNode<'a>>,
45    pub location: Location,
46}
47
48/// A `CurvedApostrophe` represents a curved apostrophe section of text in a document.
49#[derive(Clone, Debug, PartialEq)]
50pub struct CurvedApostrophe<'a> {
51    pub role: Option<Role<'a>>,
52    pub id: Option<&'a str>,
53    pub form: Form,
54    pub content: Vec<InlineNode<'a>>,
55    pub location: Location,
56}
57
58/// A `StandaloneCurvedApostrophe` represents a standalone curved apostrophe character.
59#[derive(Clone, Debug, PartialEq)]
60pub struct StandaloneCurvedApostrophe {
61    pub location: Location,
62}
63
64/// A `Monospace` represents a monospace section of text in a document.
65#[derive(Clone, Debug, PartialEq)]
66pub struct Monospace<'a> {
67    pub role: Option<Role<'a>>,
68    pub id: Option<&'a str>,
69    pub form: Form,
70    pub content: Vec<InlineNode<'a>>,
71    pub location: Location,
72}
73
74/// A `Highlight` represents a highlighted section of text in a document.
75#[derive(Clone, Debug, PartialEq)]
76pub struct Highlight<'a> {
77    pub role: Option<Role<'a>>,
78    pub id: Option<&'a str>,
79    pub form: Form,
80    pub content: Vec<InlineNode<'a>>,
81    pub location: Location,
82}
83
84/// A `Bold` represents a bold section of text in a document.
85#[derive(Clone, Debug, PartialEq)]
86pub struct Bold<'a> {
87    pub role: Option<Role<'a>>,
88    pub id: Option<&'a str>,
89    pub form: Form,
90    pub content: Vec<InlineNode<'a>>,
91    pub location: Location,
92}
93
94/// An `Italic` represents an italic section of text in a document.
95#[derive(Clone, Debug, PartialEq)]
96pub struct Italic<'a> {
97    pub role: Option<Role<'a>>,
98    pub id: Option<&'a str>,
99    pub form: Form,
100    pub content: Vec<InlineNode<'a>>,
101    pub location: Location,
102}
103
104/// A `LineBreak` represents a line break (inline).
105#[derive(Clone, Debug, PartialEq)]
106pub struct LineBreak {
107    pub location: Location,
108}
109
110impl Serialize for LineBreak {
111    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112    where
113        S: Serializer,
114    {
115        let mut state = serializer.serialize_map(Some(3))?;
116        state.serialize_entry("name", "linebreak")?;
117        state.serialize_entry("type", "string")?;
118        state.serialize_entry("location", &self.location)?;
119        state.end()
120    }
121}
122
123/// Ordinary inline text that inherits processing from its enclosing block.
124///
125/// Converters apply the enclosing block's substitutions and normal prose
126/// whitespace rules. Use [`Raw`] for text whose passthrough defines its own
127/// substitutions, or [`Verbatim`] for literal and listing content.
128#[derive(Clone, Debug, PartialEq)]
129pub struct Plain<'a> {
130    pub content: &'a str,
131    pub location: Location,
132    /// Whether an escape prevented this text from being parsed as inline formatting.
133    ///
134    /// When set, quote substitution must not reinterpret the content.
135    pub escaped: bool,
136}
137
138impl Serialize for Plain<'_> {
139    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
140    where
141        S: Serializer,
142    {
143        let mut state = serializer.serialize_map(Some(4))?;
144        state.serialize_entry("name", "text")?;
145        state.serialize_entry("type", "string")?;
146        state.serialize_entry("value", &self.content)?;
147        state.serialize_entry("location", &self.location)?;
148        state.end()
149    }
150}
151
152/// Inline passthrough text with its own substitution policy.
153///
154/// Unlike [`Plain`], this text does not inherit substitutions from its enclosing
155/// block. Unlike [`Verbatim`], it is not implicitly literal or code content. An
156/// empty [`Raw::subs`] preserves the passthrough content using the backend's raw
157/// output behavior.
158#[derive(Clone, Debug, PartialEq)]
159pub struct Raw<'a> {
160    pub content: &'a str,
161    pub location: Location,
162    /// The passthrough substitutions that the converter applies to this text, in order.
163    ///
164    /// These replace, rather than extend, the enclosing block's substitutions.
165    /// The parser resolves substitutions that create inline structure before it
166    /// returns the AST, so parsed nodes normally retain only substitutions that
167    /// require converter-specific rendering. Repeated entries remain significant. An
168    /// empty list means no further substitutions, as with `+++text+++` and `pass:[text]`.
169    pub subs: Vec<Substitution>,
170}
171
172/// Text from a literal, listing, or source context.
173///
174/// Verbatim text inherits substitutions from its enclosing block like [`Plain`],
175/// but converters use literal or code presentation and preserve source whitespace
176/// unless a requested substitution transforms it. Parsed callout markers are
177/// represented by separate [`CalloutRef`] nodes rather than remaining in this text.
178#[derive(Clone, Debug, PartialEq)]
179pub struct Verbatim<'a> {
180    pub content: &'a str,
181    pub location: Location,
182}
183
184impl Serialize for StandaloneCurvedApostrophe {
185    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
186    where
187        S: Serializer,
188    {
189        let mut state = serializer.serialize_map(Some(3))?;
190        state.serialize_entry("name", "curved_apostrophe")?;
191        state.serialize_entry("type", "string")?;
192        state.serialize_entry("location", &self.location)?;
193        state.end()
194    }
195}
196
197/// The kind of callout reference marker (how it was expressed in the source).
198#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)]
199#[serde(rename_all = "lowercase")]
200pub enum CalloutRefKind {
201    /// Explicit callout: `<1>`, `<2>`, etc. - the number was specified directly.
202    Explicit,
203    /// Auto-numbered callout: `<.>` - the number was resolved automatically.
204    Auto,
205}
206
207/// A `CalloutRef` represents a callout reference marker within verbatim content.
208///
209/// Callout references appear at the end of lines in source/listing blocks and
210/// link to explanatory text in a subsequent callout list.
211///
212/// # Examples
213///
214/// ```asciidoc
215/// [source,ruby]
216/// ----
217/// def main <1>
218///   puts 'hello' <.>
219/// end
220/// ----
221/// <1> Defines the main function
222/// <.> Prints a greeting
223/// ```
224///
225/// The `<1>` marker creates an `Explicit` callout ref, while `<.>` creates an
226/// `Auto` callout ref that gets resolved to the next available number.
227#[derive(Clone, Debug, PartialEq, Eq)]
228pub struct CalloutRef {
229    /// The kind of callout (explicit number vs auto-numbered).
230    pub kind: CalloutRefKind,
231    /// The resolved callout number (1-indexed).
232    pub number: usize,
233    /// Source location of this callout reference.
234    pub location: Location,
235}
236
237impl CalloutRef {
238    /// Creates a new explicit callout reference with the given number.
239    #[must_use]
240    pub fn explicit(number: usize, location: Location) -> Self {
241        Self {
242            kind: CalloutRefKind::Explicit,
243            number,
244            location,
245        }
246    }
247
248    /// Creates a new auto-numbered callout reference with the resolved number.
249    #[must_use]
250    pub fn auto(number: usize, location: Location) -> Self {
251        Self {
252            kind: CalloutRefKind::Auto,
253            number,
254            location,
255        }
256    }
257}
258
259impl Serialize for CalloutRef {
260    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
261    where
262        S: Serializer,
263    {
264        let mut state = serializer.serialize_map(Some(5))?;
265        state.serialize_entry("name", "callout_reference")?;
266        state.serialize_entry("type", "inline")?;
267        state.serialize_entry("variant", &self.kind)?;
268        state.serialize_entry("number", &self.number)?;
269        state.serialize_entry("location", &self.location)?;
270        state.end()
271    }
272}