Skip to main content

brief/
ast.rs

1use crate::shortcode::ArgValue;
2use crate::span::Span;
3use std::collections::BTreeMap;
4
5#[derive(Clone, Debug, Default)]
6pub struct Document {
7    pub blocks: Vec<Block>,
8    pub metadata: Option<toml::Table>,
9    /// Validated `@ref` invocations, keyed by source span. Populated by
10    /// `resolve_with_project`. Empty when the per-file resolver runs
11    /// without project context.
12    pub resolved_refs: std::collections::BTreeMap<crate::span::Span, ResolvedRef>,
13}
14
15#[derive(Clone, Debug)]
16pub struct ResolvedRef {
17    /// Project-relative target file as a forward-slash-separated string,
18    /// validated by `resolve::parse_target`. Always ends in `.brf`.
19    pub target_path: String,
20    /// Anchor in the target file, or None for a whole-file reference.
21    pub target_anchor: Option<String>,
22    /// Display text (taken from the `title` positional arg).
23    pub display: String,
24}
25
26#[derive(Clone, Debug)]
27pub enum Block {
28    Heading {
29        level: u8,
30        content: Vec<Inline>,
31        anchor: Option<String>,
32        span: Span,
33    },
34    Paragraph {
35        content: Vec<Inline>,
36        span: Span,
37    },
38    List {
39        ordered: bool,
40        items: Vec<ListItem>,
41        span: Span,
42    },
43    Blockquote {
44        children: Vec<Block>,
45        span: Span,
46    },
47    CodeBlock {
48        lang: Option<String>,
49        body: String,
50        attrs: CodeAttrs,
51        span: Span,
52    },
53    Table {
54        args: ShortArgs,
55        header: Row,
56        rows: Vec<Row>,
57        span: Span,
58    },
59    DefinitionList {
60        args: ShortArgs,
61        items: Vec<DefinitionItem>,
62        span: Span,
63    },
64    BlockShortcode {
65        name: String,
66        args: ShortArgs,
67        children: Vec<Block>,
68        span: Span,
69    },
70    HorizontalRule {
71        span: Span,
72    },
73}
74
75#[derive(Clone, Debug, Default, PartialEq, Eq)]
76pub struct CodeAttrs {
77    pub nominify: bool,
78    pub minify: bool,
79    /// `@minify-keep-comments` — State 3. Implies minification but the
80    /// minifier preserves comments (converting `//` to `/* */` where
81    /// necessary, with a B0703 warning per conversion).
82    pub keep_comments: bool,
83}
84
85#[derive(Clone, Copy, Debug, PartialEq, Eq)]
86pub enum TaskState {
87    Done,
88    Todo,
89}
90
91#[derive(Clone, Debug)]
92pub struct ListItem {
93    pub content: Vec<Inline>,
94    pub children: Vec<Block>,
95    /// Set when the item begins with the literal `[x] ` (Done) or `[ ] `
96    /// (Todo) marker. Marker bytes are consumed by the parser; `content`
97    /// holds the rest. Both unordered and ordered list items can carry this.
98    pub task: Option<TaskState>,
99    pub span: Span,
100}
101
102#[derive(Clone, Debug)]
103pub struct Row {
104    pub cells: Vec<Vec<Inline>>,
105    pub span: Span,
106}
107
108#[derive(Clone, Debug)]
109pub struct DefinitionItem {
110    pub term: Vec<Inline>,
111    pub definition: Vec<Inline>,
112    pub span: Span,
113}
114
115#[derive(Clone, Debug)]
116pub enum Inline {
117    Text {
118        value: String,
119        span: Span,
120    },
121    Bold {
122        content: Vec<Inline>,
123        span: Span,
124    },
125    Italic {
126        content: Vec<Inline>,
127        span: Span,
128    },
129    Underline {
130        content: Vec<Inline>,
131        span: Span,
132    },
133    Strike {
134        content: Vec<Inline>,
135        span: Span,
136    },
137    InlineCode {
138        value: String,
139        span: Span,
140    },
141    HardBreak {
142        span: Span,
143    },
144    Shortcode {
145        name: String,
146        args: ShortArgs,
147        content: Option<Vec<Inline>>,
148        span: Span,
149    },
150}
151
152#[derive(Clone, Debug, Default)]
153pub struct ShortArgs {
154    pub positional: Vec<ArgValue>,
155    pub keyword: BTreeMap<String, ArgValue>,
156}
157
158impl ShortArgs {
159    pub fn is_empty(&self) -> bool {
160        self.positional.is_empty() && self.keyword.is_empty()
161    }
162}