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 span: Span,
48    pub children: Vec<'a, Document<'a>>,
49    /// Every comment in the stream, in source order. Comments are not
50    /// attached to nodes; consumers place them positionally via spans
51    /// (the comment-cursor pattern).
52    pub comments: Vec<'a, Comment>,
53}
54
55#[derive(Debug)]
56#[expect(clippy::struct_field_names)] // mirrors yaml-unist-parser's field names
57pub struct Document<'a> {
58    pub span: Span,
59    pub head: DocumentHead<'a>,
60    pub body: DocumentBody<'a>,
61    /// Span of the `---` marker if present.
62    pub directives_end_marker: Option<Span>,
63    /// Span of the `...` marker if present.
64    pub document_end_marker: Option<Span>,
65}
66
67#[derive(Debug)]
68pub struct DocumentHead<'a> {
69    pub span: Span,
70    pub directives: Vec<'a, Directive<'a>>,
71}
72
73#[derive(Debug)]
74pub struct DocumentBody<'a> {
75    pub span: Span,
76    pub content: Option<Content<'a>>,
77}
78
79/// `%NAME param param`. Uninterpreted; `%YAML`/`%TAG`/unknown are all accepted.
80#[derive(Debug)]
81pub struct Directive<'a> {
82    pub span: Span,
83    pub name: &'a str,
84    pub parameters: Vec<'a, &'a str>,
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 span: Span,
137    pub props: Props,
138}
139
140/// `'...'`. `span` includes the quotes.
141#[derive(Debug)]
142pub struct QuoteSingle {
143    pub span: Span,
144    pub props: Props,
145}
146
147/// `"..."`. `span` includes the quotes.
148#[derive(Debug)]
149pub struct QuoteDouble {
150    pub span: Span,
151    pub props: Props,
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 span: Span,
172    pub props: Props,
173    pub chomping: Chomping,
174    /// Explicit indentation indicator digit, if any.
175    pub indent: Option<u32>,
176    /// Offset right after the header line's line break (= where content
177    /// scanning began). The content is `content_start..span.end`.
178    pub content_start: u32,
179}
180
181/// A block mapping.
182#[derive(Debug)]
183pub struct Mapping<'a> {
184    pub span: Span,
185    pub props: Props,
186    pub children: Vec<'a, MappingItem<'a>>,
187}
188
189/// One `key: value` pair in a block mapping.
190#[derive(Debug)]
191pub struct MappingItem<'a> {
192    pub span: Span,
193    pub key: MappingKey<'a>,
194    pub value: MappingValue<'a>,
195}
196
197#[derive(Debug)]
198pub struct MappingKey<'a> {
199    pub span: Span,
200    /// `None` for a value-less key position (`: value` with explicit `?`, or empty).
201    pub content: Option<Content<'a>>,
202    /// `true` when written with the explicit `?` indicator.
203    pub explicit: bool,
204}
205
206#[derive(Debug)]
207pub struct MappingValue<'a> {
208    pub span: Span,
209    /// `None` for `key:` with no value.
210    pub content: Option<Content<'a>>,
211}
212
213/// A block sequence.
214#[derive(Debug)]
215pub struct Sequence<'a> {
216    pub span: Span,
217    pub props: Props,
218    pub children: Vec<'a, SequenceItem<'a>>,
219}
220
221/// One `- item` in a block sequence. `span` starts at the `-`.
222#[derive(Debug)]
223pub struct SequenceItem<'a> {
224    pub span: Span,
225    pub content: Option<Content<'a>>,
226}
227
228/// `{ ... }`.
229#[derive(Debug)]
230pub struct FlowMapping<'a> {
231    pub span: Span,
232    pub props: Props,
233    pub children: Vec<'a, MappingItem<'a>>,
234}
235
236/// `[ ... ]`.
237#[derive(Debug)]
238pub struct FlowSequence<'a> {
239    pub span: Span,
240    pub props: Props,
241    pub children: Vec<'a, FlowSequenceEntry<'a>>,
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 span: Span,
255    pub content: Content<'a>,
256}
257
258/// `*name`. `span` covers the `*` and the name.
259#[derive(Debug)]
260pub struct Alias {
261    pub span: Span,
262    pub props: Props,
263}