1use rustc_hash::FxHashMap;
2
3pub type BlockId = u32;
5
6pub type DocumentId = u32;
8
9#[derive(Debug, Clone, PartialEq)]
11pub struct Span {
12 pub start_line: usize,
13 pub start_col: usize,
14 pub end_line: usize,
15 pub end_col: usize,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash)]
20pub enum BlockType {
21 Heading,
23 Paragraph,
25 Code,
27 List,
29 TableCell,
31 TableRow,
33 TableAlign,
35 Blockquote,
37 HorizontalRule,
39 Html,
41 Yaml,
43 Toml,
45 Math,
47 Definition,
49 Footnote,
51}
52
53impl BlockType {
54 pub fn as_str(&self) -> &'static str {
55 match self {
56 BlockType::Heading => "heading",
57 BlockType::Paragraph => "paragraph",
58 BlockType::Code => "code",
59 BlockType::List => "list",
60 BlockType::TableCell => "table_cell",
61 BlockType::TableRow => "table_row",
62 BlockType::TableAlign => "table_align",
63 BlockType::Blockquote => "blockquote",
64 BlockType::HorizontalRule => "horizontal_rule",
65 BlockType::Html => "html",
66 BlockType::Yaml => "yaml",
67 BlockType::Toml => "toml",
68 BlockType::Math => "math",
69 BlockType::Definition => "definition",
70 BlockType::Footnote => "footnote",
71 }
72 }
73}
74
75impl std::fmt::Display for BlockType {
76 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
77 write!(f, "{}", self.as_str())
78 }
79}
80
81#[derive(Debug, Clone, PartialEq)]
90pub enum PropertyValue {
91 String(String),
92 Int(i64),
93 Float(f64),
94 Bool(bool),
95 Array(Vec<PropertyValue>),
96 Null,
97}
98
99impl PropertyValue {
100 pub fn as_str(&self) -> Option<&str> {
101 if let Self::String(s) = self {
102 Some(s)
103 } else {
104 None
105 }
106 }
107
108 pub fn as_int(&self) -> Option<i64> {
109 if let Self::Int(i) = self {
110 Some(*i)
111 } else {
112 None
113 }
114 }
115
116 pub fn as_bool(&self) -> Option<bool> {
117 if let Self::Bool(b) = self {
118 Some(*b)
119 } else {
120 None
121 }
122 }
123
124 pub fn as_array(&self) -> Option<&[PropertyValue]> {
125 if let Self::Array(a) = self {
126 Some(a)
127 } else {
128 None
129 }
130 }
131}
132
133impl From<String> for PropertyValue {
134 fn from(s: String) -> Self {
135 Self::String(s)
136 }
137}
138
139impl From<&str> for PropertyValue {
140 fn from(s: &str) -> Self {
141 Self::String(s.to_string())
142 }
143}
144
145impl From<i64> for PropertyValue {
146 fn from(i: i64) -> Self {
147 Self::Int(i)
148 }
149}
150
151impl From<u8> for PropertyValue {
152 fn from(i: u8) -> Self {
153 Self::Int(i as i64)
154 }
155}
156
157impl From<u32> for PropertyValue {
158 fn from(i: u32) -> Self {
159 Self::Int(i as i64)
160 }
161}
162
163impl From<usize> for PropertyValue {
164 fn from(i: usize) -> Self {
165 Self::Int(i as i64)
166 }
167}
168
169impl From<bool> for PropertyValue {
170 fn from(b: bool) -> Self {
171 Self::Bool(b)
172 }
173}
174
175#[derive(Debug, Clone, Default, PartialEq)]
178pub struct Properties(FxHashMap<String, PropertyValue>);
179
180impl Properties {
181 pub fn new() -> Self {
182 Self::default()
183 }
184
185 pub fn get(&self, key: &str) -> Option<&PropertyValue> {
186 self.0.get(key)
187 }
188
189 pub fn set(&mut self, key: impl Into<String>, value: impl Into<PropertyValue>) {
190 self.0.insert(key.into(), value.into());
191 }
192
193 pub fn contains_key(&self, key: &str) -> bool {
194 self.0.contains_key(key)
195 }
196
197 pub fn iter(&self) -> impl Iterator<Item = (&String, &PropertyValue)> {
198 self.0.iter()
199 }
200}
201
202#[derive(Debug, Clone, PartialEq)]
216pub struct Block {
217 pub id: BlockId,
218 pub document_id: DocumentId,
219 pub block_type: BlockType,
220 pub content: String,
222 pub span: Option<Span>,
224 pub pre: u32,
226 pub post: u32,
228 pub properties: Properties,
230}
231
232impl Block {
233 pub fn contains(&self, other: &Block) -> bool {
238 self.pre < other.pre && other.post < self.post
239 }
240
241 pub fn is_under(&self, ancestor: &Block) -> bool {
243 ancestor.pre < self.pre && self.post < ancestor.post
244 }
245
246 pub fn is_under_interval(&self, anc_pre: u32, anc_post: u32) -> bool {
249 anc_pre < self.pre && self.post < anc_post
250 }
251
252 pub fn heading_depth(&self) -> Option<u8> {
254 self.properties.get("depth")?.as_int().map(|d| d as u8)
255 }
256
257 pub fn code_lang(&self) -> Option<&str> {
259 self.properties.get("lang")?.as_str()
260 }
261}