Skip to main content

oxc_yaml_parser/
ast.rs

1//! AST node definitions.
2//!
3//! The node shapes mirror [yaml-unist-parser](https://github.com/prettier/yaml-unist-parser)'s
4//! unist AST — the AST Prettier's YAML printer consumes — to keep a
5//! Prettier-compatible printer close to its reference.
6//!
7//! Two deliberate departures:
8//! - Scalar nodes do not carry cooked values; consumers slice the original
9//!   source through [`Span`]s.
10//! - Comments are not attached to nodes (yaml-unist-parser's
11//!   leading/middle/trailing/end comment fields have no counterpart here).
12//!   They live in [`Root::comments`] in source order; consumers place them
13//!   positionally via spans (the comment-cursor pattern used by the other
14//!   oxc formatters).
15
16use crate::pos::Span;
17use oxc_allocator::{Box, Vec};
18
19/// A `#` comment. `span` covers `#` through the end of the comment text.
20#[derive(Clone, Copy, Debug)]
21pub struct Comment {
22    pub span: Span,
23}
24
25/// `&name`. `span` covers the `&` and the name.
26#[derive(Clone, Copy, Debug)]
27pub struct Anchor {
28    pub span: Span,
29}
30
31/// A tag property: `!`, `!suffix`, `!handle!suffix`, `!!suffix` or `!<verbatim>`.
32#[derive(Clone, Copy, Debug)]
33pub struct Tag {
34    pub span: Span,
35}
36
37/// Properties shared by every content node (mirrors yaml-unist-parser's `Content`).
38#[derive(Clone, Copy, Debug)]
39pub struct Props {
40    pub anchor: Option<Anchor>,
41    pub tag: Option<Tag>,
42}
43
44/// The whole stream.
45#[derive(Debug)]
46pub struct Root<'a> {
47    pub children: Vec<'a, Document<'a>>,
48    /// Every comment in the stream, in source order. Comments are not
49    /// attached to nodes; consumers place them positionally via spans
50    /// (the comment-cursor pattern).
51    pub comments: Vec<'a, Comment>,
52    pub span: Span,
53}
54
55#[derive(Debug)]
56#[expect(clippy::struct_field_names)] // mirrors yaml-unist-parser's field names
57pub struct Document<'a> {
58    pub head: DocumentHead<'a>,
59    pub body: DocumentBody<'a>,
60    /// Span of the `---` marker if present.
61    pub directives_end_marker: Option<Span>,
62    /// Span of the `...` marker if present.
63    pub document_end_marker: Option<Span>,
64    pub span: Span,
65}
66
67#[derive(Debug)]
68pub struct DocumentHead<'a> {
69    pub directives: Vec<'a, Directive<'a>>,
70    pub span: Span,
71}
72
73#[derive(Debug)]
74pub struct DocumentBody<'a> {
75    pub content: Option<Content<'a>>,
76    pub span: Span,
77}
78
79/// `%NAME param param`. Uninterpreted; `%YAML`/`%TAG`/unknown are all accepted.
80#[derive(Debug)]
81pub struct Directive<'a> {
82    pub name: &'a str,
83    pub parameters: Vec<'a, &'a str>,
84    pub span: Span,
85}
86
87/// A content node (mirrors yaml-unist-parser's `ContentNode`).
88#[derive(Debug)]
89pub enum Content<'a> {
90    Plain(Box<'a, Plain>),
91    QuoteSingle(Box<'a, QuoteSingle>),
92    QuoteDouble(Box<'a, QuoteDouble>),
93    BlockLiteral(Box<'a, BlockScalar>),
94    BlockFolded(Box<'a, BlockScalar>),
95    Mapping(Box<'a, Mapping<'a>>),
96    Sequence(Box<'a, Sequence<'a>>),
97    FlowMapping(Box<'a, FlowMapping<'a>>),
98    FlowSequence(Box<'a, FlowSequence<'a>>),
99    Alias(Box<'a, Alias>),
100}
101
102impl Content<'_> {
103    pub fn span(&self) -> Span {
104        match self {
105            Content::Plain(n) => n.span,
106            Content::QuoteSingle(n) => n.span,
107            Content::QuoteDouble(n) => n.span,
108            Content::BlockLiteral(n) | Content::BlockFolded(n) => n.span,
109            Content::Mapping(n) => n.span,
110            Content::Sequence(n) => n.span,
111            Content::FlowMapping(n) => n.span,
112            Content::FlowSequence(n) => n.span,
113            Content::Alias(n) => n.span,
114        }
115    }
116
117    pub fn props(&self) -> &Props {
118        match self {
119            Content::Plain(n) => &n.props,
120            Content::QuoteSingle(n) => &n.props,
121            Content::QuoteDouble(n) => &n.props,
122            Content::BlockLiteral(n) | Content::BlockFolded(n) => &n.props,
123            Content::Mapping(n) => &n.props,
124            Content::Sequence(n) => &n.props,
125            Content::FlowMapping(n) => &n.props,
126            Content::FlowSequence(n) => &n.props,
127            Content::Alias(n) => &n.props,
128        }
129    }
130}
131
132/// A plain (unquoted) scalar. `span` covers the raw scalar text
133/// (trailing whitespace/comments excluded).
134#[derive(Debug)]
135pub struct Plain {
136    pub props: Props,
137    pub span: Span,
138}
139
140/// `'...'`. `span` includes the quotes.
141#[derive(Debug)]
142pub struct QuoteSingle {
143    pub props: Props,
144    pub span: Span,
145}
146
147/// `"..."`. `span` includes the quotes.
148#[derive(Debug)]
149pub struct QuoteDouble {
150    pub props: Props,
151    pub span: Span,
152}
153
154#[derive(Clone, Copy, Debug, PartialEq, Eq)]
155pub enum Chomping {
156    /// (default) single trailing newline
157    Clip,
158    /// `+` keep all trailing newlines
159    Keep,
160    /// `-` strip all trailing newlines
161    Strip,
162}
163
164/// `|` (literal) or `>` (folded) block scalar.
165///
166/// The variant is distinguished by the enclosing [`Content`] variant. `span`
167/// covers the indicator through the end of the content (including trailing
168/// line breaks consumed while scanning).
169#[derive(Debug)]
170pub struct BlockScalar {
171    pub props: Props,
172    pub chomping: Chomping,
173    /// Explicit indentation indicator digit, if any.
174    pub indent: Option<u32>,
175    /// Offset right after the header line's line break (= where content
176    /// scanning began). The content is `content_start..span.end`.
177    pub content_start: u32,
178    pub span: Span,
179}
180
181/// A block mapping.
182#[derive(Debug)]
183pub struct Mapping<'a> {
184    pub props: Props,
185    pub children: Vec<'a, MappingItem<'a>>,
186    pub span: Span,
187}
188
189/// One `key: value` pair in a block mapping.
190#[derive(Debug)]
191pub struct MappingItem<'a> {
192    pub key: MappingKey<'a>,
193    pub value: MappingValue<'a>,
194    pub span: Span,
195}
196
197#[derive(Debug)]
198pub struct MappingKey<'a> {
199    /// `None` for a value-less key position (`: value` with explicit `?`, or empty).
200    pub content: Option<Content<'a>>,
201    /// `true` when written with the explicit `?` indicator.
202    pub explicit: bool,
203    pub span: Span,
204}
205
206#[derive(Debug)]
207pub struct MappingValue<'a> {
208    /// `None` for `key:` with no value.
209    pub content: Option<Content<'a>>,
210    pub span: Span,
211}
212
213/// A block sequence.
214#[derive(Debug)]
215pub struct Sequence<'a> {
216    pub props: Props,
217    pub children: Vec<'a, SequenceItem<'a>>,
218    pub span: Span,
219}
220
221/// One `- item` in a block sequence. `span` starts at the `-`.
222#[derive(Debug)]
223pub struct SequenceItem<'a> {
224    pub content: Option<Content<'a>>,
225    pub span: Span,
226}
227
228/// `{ ... }`.
229#[derive(Debug)]
230pub struct FlowMapping<'a> {
231    pub props: Props,
232    pub children: Vec<'a, MappingItem<'a>>,
233    pub span: Span,
234}
235
236/// `[ ... ]`.
237#[derive(Debug)]
238pub struct FlowSequence<'a> {
239    pub props: Props,
240    pub children: Vec<'a, FlowSequenceEntry<'a>>,
241    pub span: Span,
242}
243
244/// An entry in a flow sequence: a plain item, or a `key: value` pair.
245/// The pair is boxed so plain items don't pay for the larger variant.
246#[derive(Debug)]
247pub enum FlowSequenceEntry<'a> {
248    Item(FlowSequenceItem<'a>),
249    Pair(Box<'a, MappingItem<'a>>),
250}
251
252#[derive(Debug)]
253pub struct FlowSequenceItem<'a> {
254    pub content: Content<'a>,
255    pub span: Span,
256}
257
258/// `*name`. `span` covers the `*` and the name.
259#[derive(Debug)]
260pub struct Alias {
261    pub props: Props,
262    pub span: Span,
263}