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 > user
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    /// #user 참조 결과 (title만, URL은 렌더러에서 조립)
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub user: Option<ResolvedDoc>,
76    /// #url 외부 링크
77    #[serde(skip_serializing_if = "Option::is_none")]
78    pub url: Option<String>,
79}
80
81/// 메인 SevenMark AST Element enum
82#[derive(Debug, Clone, Serialize)]
83pub enum Element {
84    // Basic text elements
85    Text(TextElement),
86    Comment(CommentElement),
87    Escape(EscapeElement),
88    Error(ErrorElement),
89
90    // Block elements
91    Literal(LiteralElement),
92    Define(DefineElement),
93    Styled(StyledElement),
94    Table(TableElement),
95    List(ListElement),
96    Fold(FoldElement),
97    BlockQuote(BlockQuoteElement),
98    Ruby(RubyElement),
99    Footnote(FootnoteElement),
100    Code(CodeElement),
101    TeX(TeXElement),
102
103    // Wiki elements
104    Include(IncludeElement),
105    Category(CategoryElement),
106    Redirect(RedirectElement),
107
108    // Media
109    Media(MediaElement),
110    ExternalMedia(ExternalMediaElement),
111
112    // Macros (leaf nodes)
113    Null(NullElement),
114    FootnoteRef(FootnoteRefElement),
115    TimeNow(TimeNowElement),
116    Age(AgeElement),
117    Variable(VariableElement),
118    Mention(MentionElement),
119
120    // Text styles
121    Bold(TextStyleElement),
122    Italic(TextStyleElement),
123    Strikethrough(TextStyleElement),
124    Underline(TextStyleElement),
125    Superscript(TextStyleElement),
126    Subscript(TextStyleElement),
127
128    // Line elements
129    SoftBreak(SoftBreakElement),
130    HardBreak(HardBreakElement),
131    HLine(HLineElement),
132    Header(HeaderElement),
133
134    // Conditional
135    If(IfElement),
136}
137
138impl Element {
139    /// Returns the span of this element
140    pub fn span(&self) -> &Span {
141        match self {
142            Element::Text(e) => &e.span,
143            Element::Comment(e) => &e.span,
144            Element::Escape(e) => &e.span,
145            Element::Error(e) => &e.span,
146            Element::Literal(e) => &e.span,
147            Element::Define(e) => &e.span,
148            Element::Styled(e) => &e.span,
149            Element::Table(e) => &e.span,
150            Element::List(e) => &e.span,
151            Element::Fold(e) => &e.span,
152            Element::BlockQuote(e) => &e.span,
153            Element::Ruby(e) => &e.span,
154            Element::Footnote(e) => &e.span,
155            Element::Code(e) => &e.span,
156            Element::TeX(e) => &e.span,
157            Element::Include(e) => &e.span,
158            Element::Category(e) => &e.span,
159            Element::Redirect(e) => &e.span,
160            Element::Media(e) => &e.span,
161            Element::ExternalMedia(e) => &e.span,
162            Element::Null(e) => &e.span,
163            Element::FootnoteRef(e) => &e.span,
164            Element::TimeNow(e) => &e.span,
165            Element::Age(e) => &e.span,
166            Element::Variable(e) => &e.span,
167            Element::Mention(e) => &e.span,
168            Element::Bold(e)
169            | Element::Italic(e)
170            | Element::Strikethrough(e)
171            | Element::Underline(e)
172            | Element::Superscript(e)
173            | Element::Subscript(e) => &e.span,
174            Element::SoftBreak(e) => &e.span,
175            Element::HardBreak(e) => &e.span,
176            Element::HLine(e) => &e.span,
177            Element::Header(e) => &e.span,
178            Element::If(e) => &e.span,
179        }
180    }
181}