Skip to main content

sevenmark_parser/ast/
mod.rs

1//! AST (Abstract Syntax Tree) definitions for SevenMark
2//!
3//! This module contains all AST element definitions organized into submodules:
4//! - `span`: Span and Parameter types
5//! - `expression`: Expression AST for conditionals
6//! - `elements`: Individual element structs
7//! - `table`: Table-related structures
8//! - `list`: List-related structures
9//! - `traversable`: Traversable trait and implementation
10
11mod elements;
12mod expression;
13mod list;
14mod span;
15mod table;
16mod traversable;
17
18// Re-export all public types
19pub use elements::*;
20pub use expression::*;
21pub use list::*;
22pub use span::*;
23pub use table::*;
24pub use traversable::*;
25
26use serde::Serialize;
27
28// === Helper types ===
29
30/// 멘션 타입
31#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
32pub enum MentionType {
33    /// 토론/문서 멘션 (<#uuid>)
34    Discussion,
35    /// 사용자 멘션 (<@uuid>)
36    User,
37}
38
39/// 파일 resolve 결과 (DB에서 실제 URL을 가져옴)
40#[derive(Debug, Clone, Serialize, Default)]
41pub struct ResolvedFile {
42    pub url: String,
43    pub is_valid: bool,
44    /// 이미지 너비 (CLS 개선용)
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub width: Option<u32>,
47    /// 이미지 높이 (CLS 개선용)
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub height: Option<u32>,
50}
51
52/// 문서/카테고리 resolve 결과 (title만 저장, URL은 렌더러에서 조립)
53#[derive(Debug, Clone, Serialize, Default)]
54pub struct ResolvedDoc {
55    pub title: String,
56    pub is_valid: bool,
57}
58
59/// MediaElement resolve 결과
60/// file, document, category, url 각각 독립적으로 처리
61/// href 우선순위: url > document > category
62#[derive(Debug, Clone, Serialize, Default)]
63pub struct ResolvedMediaInfo {
64    /// #file 참조 결과 (이미지 표시용, DB에서 실제 URL)
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub file: Option<ResolvedFile>,
67    /// #document 참조 결과 (title만, URL은 렌더러에서 조립)
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub document: Option<ResolvedDoc>,
70    /// #category 참조 결과 (title만, URL은 렌더러에서 조립)
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub category: Option<ResolvedDoc>,
73    /// #url 외부 링크
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub url: Option<String>,
76}
77
78/// 메인 SevenMark AST Element enum
79#[derive(Debug, Clone, Serialize)]
80pub enum Element {
81    // Basic text elements
82    Text(TextElement),
83    Comment(CommentElement),
84    Escape(EscapeElement),
85    Error(ErrorElement),
86
87    // Block elements
88    Literal(LiteralElement),
89    Define(DefineElement),
90    Styled(StyledElement),
91    Table(TableElement),
92    List(ListElement),
93    Fold(FoldElement),
94    BlockQuote(BlockQuoteElement),
95    Ruby(RubyElement),
96    Footnote(FootnoteElement),
97    Code(CodeElement),
98    TeX(TeXElement),
99
100    // Wiki elements
101    Include(IncludeElement),
102    Category(CategoryElement),
103    Redirect(RedirectElement),
104
105    // Media
106    Media(MediaElement),
107    ExternalMedia(ExternalMediaElement),
108
109    // Macros (leaf nodes)
110    Null(NullElement),
111    FootnoteRef(FootnoteRefElement),
112    TimeNow(TimeNowElement),
113    Age(AgeElement),
114    Variable(VariableElement),
115    Mention(MentionElement),
116
117    // Text styles
118    Bold(TextStyleElement),
119    Italic(TextStyleElement),
120    Strikethrough(TextStyleElement),
121    Underline(TextStyleElement),
122    Superscript(TextStyleElement),
123    Subscript(TextStyleElement),
124
125    // Line elements
126    SoftBreak(SoftBreakElement),
127    HardBreak(HardBreakElement),
128    HLine(HLineElement),
129    Header(HeaderElement),
130
131    // Conditional
132    If(IfElement),
133}
134
135impl Element {
136    /// Returns the span of this element
137    pub fn span(&self) -> &Span {
138        match self {
139            Element::Text(e) => &e.span,
140            Element::Comment(e) => &e.span,
141            Element::Escape(e) => &e.span,
142            Element::Error(e) => &e.span,
143            Element::Literal(e) => &e.span,
144            Element::Define(e) => &e.span,
145            Element::Styled(e) => &e.span,
146            Element::Table(e) => &e.span,
147            Element::List(e) => &e.span,
148            Element::Fold(e) => &e.span,
149            Element::BlockQuote(e) => &e.span,
150            Element::Ruby(e) => &e.span,
151            Element::Footnote(e) => &e.span,
152            Element::Code(e) => &e.span,
153            Element::TeX(e) => &e.span,
154            Element::Include(e) => &e.span,
155            Element::Category(e) => &e.span,
156            Element::Redirect(e) => &e.span,
157            Element::Media(e) => &e.span,
158            Element::ExternalMedia(e) => &e.span,
159            Element::Null(e) => &e.span,
160            Element::FootnoteRef(e) => &e.span,
161            Element::TimeNow(e) => &e.span,
162            Element::Age(e) => &e.span,
163            Element::Variable(e) => &e.span,
164            Element::Mention(e) => &e.span,
165            Element::Bold(e)
166            | Element::Italic(e)
167            | Element::Strikethrough(e)
168            | Element::Underline(e)
169            | Element::Superscript(e)
170            | Element::Subscript(e) => &e.span,
171            Element::SoftBreak(e) => &e.span,
172            Element::HardBreak(e) => &e.span,
173            Element::HLine(e) => &e.span,
174            Element::Header(e) => &e.span,
175            Element::If(e) => &e.span,
176        }
177    }
178}