1use std::path::PathBuf;
6
7#[derive(Clone, Debug, Default)]
9pub struct Document {
10 pub blocks: Vec<Block>,
12}
13
14#[derive(Clone, Debug)]
16pub enum Block {
17 Heading {
19 level: u8,
21 inlines: Vec<Inline>,
23 align: Align,
25 },
26 Paragraph {
28 inlines: Vec<Inline>,
30 align: Align,
32 },
33 List(List),
35 Quote(Vec<Block>),
37 Code {
39 lang: Option<String>,
41 text: String,
43 },
44 Divider,
46 Image(BlockImage),
48 Columns(Columns),
50 Table(Table),
52}
53
54#[derive(Clone, Debug)]
56pub struct Table {
57 pub header: Option<Vec<Cell>>,
59 pub rows: Vec<Vec<Cell>>,
61 pub cols: Vec<ColSpec>,
63 pub style: TableStyle,
65}
66
67#[derive(Clone, Debug)]
69pub struct TableStyle {
70 pub pad_x: Option<f32>,
72 pub pad_y: Option<f32>,
74 pub grid: TableGrid,
76 pub header_fill: bool,
78}
79
80impl Default for TableStyle {
81 fn default() -> Self {
82 Self { pad_x: None, pad_y: None, grid: TableGrid::default(), header_fill: true }
83 }
84}
85
86#[derive(Clone, Copy, Debug)]
88pub struct TableGrid {
89 pub outer: bool,
91 pub vertical: bool,
93 pub horizontal: bool,
95}
96
97impl Default for TableGrid {
98 fn default() -> Self {
99 Self { outer: true, vertical: true, horizontal: true }
100 }
101}
102
103#[derive(Clone, Debug)]
105pub struct ColSpec {
106 pub align: Align,
108 pub width: Option<Length>,
110}
111
112impl Default for ColSpec {
113 fn default() -> Self {
114 Self { align: Align::Left, width: None }
115 }
116}
117
118#[derive(Clone, Debug)]
120pub struct Cell {
121 pub inlines: Vec<Inline>,
123 pub bg: Option<Color>,
125}
126
127#[derive(Clone, Debug)]
129pub struct Columns {
130 pub cols: Vec<Column>,
132 pub gap: Option<f32>,
134}
135
136#[derive(Clone, Debug)]
138pub struct Column {
139 pub blocks: Vec<Block>,
141 pub weight: f32,
143}
144
145#[derive(Clone, Debug)]
147pub struct List {
148 pub kind: ListKind,
150 pub start: u32,
152 pub items: Vec<ListItem>,
154}
155
156#[derive(Clone, Copy, Debug, PartialEq, Eq)]
158pub enum ListKind {
159 Unordered,
161 Ordered,
163}
164
165#[derive(Clone, Debug)]
167pub struct ListItem {
168 pub blocks: Vec<Block>,
170 pub check: Option<bool>,
173}
174
175#[derive(Clone, Debug)]
177pub struct BlockImage {
178 pub src: ImageSource,
180 pub width: Option<Length>,
182 pub align: Align,
184 pub caption: Option<Vec<Inline>>,
186}
187
188#[derive(Clone, Debug)]
190pub enum Inline {
191 Text {
193 text: String,
195 style: TextStyle,
197 },
198 Code(String),
200 LineBreak,
202}
203
204#[derive(Clone, Debug, PartialEq)]
206pub struct TextStyle {
207 pub weight: Option<u16>,
210 pub italic: bool,
212 pub underline: bool,
214 pub strike: bool,
216 pub color: Option<Color>,
218 pub highlight: Option<Highlight>,
220 pub size: f32,
222 pub font: FontRole,
224 pub link: bool,
227}
228
229impl Default for TextStyle {
230 fn default() -> Self {
231 Self {
232 weight: None,
233 italic: false,
234 underline: false,
235 strike: false,
236 color: None,
237 highlight: None,
238 size: 1.0,
239 font: FontRole::Sans,
240 link: false,
241 }
242 }
243}
244
245#[derive(Clone, Copy, Debug, PartialEq, Eq)]
247pub enum Highlight {
248 Theme,
250 Custom(Color),
252}
253
254#[derive(Clone, Debug, PartialEq)]
256pub enum FontRole {
257 Sans,
259 Serif,
261 Mono,
263 Kai,
265 Named(String),
267}
268
269#[derive(Clone, Copy, Debug, PartialEq, Eq, Default)]
271pub enum Align {
272 #[default]
274 Left,
275 Center,
277 Right,
279 Justify,
281}
282
283#[derive(Clone, Copy, Debug, PartialEq, Eq)]
285pub struct Color {
286 pub r: u8,
288 pub g: u8,
290 pub b: u8,
292 pub a: u8,
294}
295
296impl Color {
297 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
299 Self { r, g, b, a: 255 }
300 }
301
302 pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
304 Self { r, g, b, a }
305 }
306
307 pub fn hex(s: &str) -> Option<Self> {
310 let h = s.strip_prefix('#').unwrap_or(s);
311 if !h.bytes().all(|b| b.is_ascii_hexdigit()) {
312 return None;
313 }
314 let n = |slice: &str| u8::from_str_radix(slice, 16).ok();
315 match h.len() {
316 3 => {
317 let b = h.as_bytes();
318 let dup = |c: u8| {
319 let d = (c as char).to_digit(16)? as u8;
320 Some(d << 4 | d)
321 };
322 Some(Self::rgb(dup(b[0])?, dup(b[1])?, dup(b[2])?))
323 }
324 6 => Some(Self::rgb(n(&h[0..2])?, n(&h[2..4])?, n(&h[4..6])?)),
325 8 => Some(Self::rgba(n(&h[0..2])?, n(&h[2..4])?, n(&h[4..6])?, n(&h[6..8])?)),
326 _ => None,
327 }
328 }
329}
330
331#[derive(Clone, Copy, Debug, PartialEq)]
333pub enum Length {
334 Px(f32),
336 Percent(f32),
338}
339
340#[derive(Clone, Debug)]
342pub enum ImageSource {
343 Bytes(Vec<u8>),
345 Path(PathBuf),
347 Named(String),
349}