1use crate::image::Image;
5use crate::list::List;
6use crate::style::{Align, Border, Color, Common, FontKey, Overflow, TextStyle};
7use crate::table::Table;
8use crate::theme::ThemeRole;
9
10#[cfg_attr(
18 feature = "serde",
19 derive(serde::Serialize, serde::Deserialize),
20 serde(tag = "type", rename_all = "snake_case")
21)]
22#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
23#[derive(Clone, Debug)]
24pub enum Element {
25 Text(Text),
26 Row(Row),
27 Column(Column),
28 Spacer(Spacer),
29 Line(Line),
30 Rect(Rect),
31 Table(Table),
32 Image(Image),
33 List(List),
34 TableOfContents(TableOfContents),
35 PageBreak,
38}
39
40macro_rules! common_accessor {
45 ($self:expr, $($ref:tt)*) => {
46 match $self {
47 Element::Text(t) => Some($($ref)* t.common),
48 Element::Row(r) => Some($($ref)* r.common),
49 Element::Column(c) => Some($($ref)* c.common),
50 Element::Line(l) => Some($($ref)* l.common),
51 Element::Rect(r) => Some($($ref)* r.common),
52 Element::Table(t) => Some($($ref)* t.common),
53 Element::Image(i) => Some($($ref)* i.common),
54 Element::List(l) => Some($($ref)* l.common),
55 Element::TableOfContents(t) => Some($($ref)* t.common),
56 Element::Spacer(_) | Element::PageBreak => None,
57 }
58 };
59}
60
61impl Element {
62 pub fn common(&self) -> Option<&Common> {
65 common_accessor!(self, &)
66 }
67
68 pub fn common_mut(&mut self) -> Option<&mut Common> {
72 common_accessor!(self, &mut)
73 }
74}
75
76macro_rules! common_builder_methods {
80 () => {
81 pub fn width(mut self, width: f32) -> Self {
82 self.common.width = Some(width);
83 self
84 }
85
86 pub fn height(mut self, height: f32) -> Self {
87 self.common.height = Some(height);
88 self
89 }
90
91 pub fn flex(mut self, factor: f32) -> Self {
92 self.common.flex = Some(factor);
93 self
94 }
95
96 pub fn padding(mut self, padding: f32) -> Self {
97 self.common.padding = padding;
98 self
99 }
100
101 pub fn corner_radius(mut self, radius: f32) -> Self {
102 self.common.corner_radius = radius;
103 self
104 }
105
106 pub fn overflow(mut self, overflow: Overflow) -> Self {
107 self.common.overflow = overflow;
108 self
109 }
110
111 pub fn background(mut self, color: Color) -> Self {
112 self.common.background = Some(color);
113 self
114 }
115
116 pub fn border(mut self, border: Border) -> Self {
117 self.common.border = Some(border);
118 self
119 }
120
121 pub fn keep_with_next(mut self) -> Self {
124 self.common.keep_with_next = true;
125 self
126 }
127 };
128}
129
130#[cfg_attr(
135 feature = "serde",
136 derive(serde::Serialize, serde::Deserialize),
137 serde(deny_unknown_fields, default)
138)]
139#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
140#[derive(Clone, Debug, Default)]
141pub struct Text {
142 pub content: String,
143 #[cfg_attr(feature = "serde", serde(default))]
144 pub style: TextStyle,
145 pub url: Option<String>,
146 pub anchor: Option<String>,
151 pub link_to: Option<String>,
155 pub outline_level: Option<u8>,
160 pub role: Option<ThemeRole>,
169 pub spans: Option<Box<Vec<Span>>>,
185 pub hyphenate: Option<HyphenationLanguage>,
196 pub common: Common,
197}
198
199#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(rename_all = "snake_case"))]
203#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
204#[derive(Clone, Copy, Debug, PartialEq, Eq)]
205pub enum HyphenationLanguage {
206 EnglishUs,
207 German,
208}
209
210#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(deny_unknown_fields))]
212#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
213#[derive(Clone, Debug)]
214pub struct Span {
215 pub text: String,
216 #[cfg_attr(feature = "serde", serde(default))]
217 pub style: TextStyle,
218}
219
220impl Span {
221 pub fn new(text: impl Into<String>, style: TextStyle) -> Self {
222 Span { text: text.into(), style }
223 }
224}
225
226impl Text {
227 pub fn new(content: impl Into<String>) -> Self {
228 Text {
229 content: content.into(),
230 style: TextStyle::default(),
231 url: None,
232 anchor: None,
233 link_to: None,
234 outline_level: None,
235 role: Some(ThemeRole::Body),
236 spans: None,
237 hyphenate: None,
238 common: Common::default(),
239 }
240 }
241
242 pub fn rich(spans: impl IntoIterator<Item = Span>) -> Self {
248 let spans: Vec<Span> = spans.into_iter().collect();
249 let content = spans.iter().map(|s| s.text.as_str()).collect::<Vec<_>>().concat();
250 let style = spans.first().map(|s| s.style).unwrap_or_default();
251 Text {
252 content,
253 style,
254 url: None,
255 anchor: None,
256 link_to: None,
257 outline_level: None,
258 role: None,
259 spans: Some(Box::new(spans)),
260 hyphenate: None,
261 common: Common::default(),
262 }
263 }
264
265 pub fn url(mut self, url: impl Into<String>) -> Self {
266 self.url = Some(url.into());
267 self
268 }
269
270 pub fn anchor(mut self, name: impl Into<String>) -> Self {
271 self.anchor = Some(name.into());
272 self
273 }
274
275 pub fn link_to(mut self, anchor: impl Into<String>) -> Self {
276 self.link_to = Some(anchor.into());
277 self
278 }
279
280 pub fn outline_level(mut self, level: u8) -> Self {
281 self.outline_level = Some(level);
282 self
283 }
284
285 pub fn hyphenate(mut self, lang: HyphenationLanguage) -> Self {
289 self.hyphenate = Some(lang);
290 self
291 }
292
293 pub fn role(mut self, role: ThemeRole) -> Self {
297 self.role = Some(role);
298 self
299 }
300
301 pub fn size(mut self, size: f32) -> Self {
302 self.style.size = size;
303 self.role = None;
304 self
305 }
306
307 pub fn bold(mut self) -> Self {
308 self.style.font = FontKey::SANS_BOLD;
309 self.role = None;
310 self
311 }
312
313 pub fn italic(mut self) -> Self {
314 self.style.font = FontKey::SANS_ITALIC;
315 self.role = None;
316 self
317 }
318
319 pub fn bold_italic(mut self) -> Self {
320 self.style.font = FontKey::SANS_BOLD_ITALIC;
321 self.role = None;
322 self
323 }
324
325 pub fn font(mut self, font: FontKey) -> Self {
326 self.style.font = font;
327 self.role = None;
328 self
329 }
330
331 pub fn color(mut self, color: Color) -> Self {
332 self.style.color = color;
333 self.role = None;
334 self
335 }
336
337 pub fn align(mut self, align: Align) -> Self {
344 self.style.align = align;
345 self
346 }
347
348 pub fn line_height(mut self, line_height: f32) -> Self {
349 self.style.line_height = line_height;
350 self.role = None;
351 self
352 }
353
354 pub fn heading1(self) -> Self {
363 self.size(24.0).bold().keep_with_next().outline_level(1).role(ThemeRole::Heading1)
364 }
365
366 pub fn heading2(self) -> Self {
367 self.size(18.0).bold().keep_with_next().outline_level(2).role(ThemeRole::Heading2)
368 }
369
370 pub fn heading3(self) -> Self {
371 self.size(14.0).bold().keep_with_next().outline_level(3).role(ThemeRole::Heading3)
372 }
373
374 pub fn caption(self) -> Self {
377 self.size(9.0).color(Color::rgb(0x66, 0x66, 0x66)).role(ThemeRole::Caption)
378 }
379
380 pub fn muted(self) -> Self {
383 self.color(Color::rgb(0x66, 0x66, 0x66)).role(ThemeRole::Muted)
384 }
385
386 pub fn table_header(self) -> Self {
391 self.bold().role(ThemeRole::TableHeader)
392 }
393
394 common_builder_methods!();
395}
396
397impl From<&str> for Text {
398 fn from(value: &str) -> Self {
399 Text::new(value)
400 }
401}
402
403impl From<String> for Text {
404 fn from(value: String) -> Self {
405 Text::new(value)
406 }
407}
408
409#[cfg_attr(
414 feature = "serde",
415 derive(serde::Serialize, serde::Deserialize),
416 serde(deny_unknown_fields, default)
417)]
418#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
419#[derive(Clone, Debug, Default)]
420pub struct Row {
421 pub children: Vec<Element>,
422 pub gap: f32,
423 pub align: Align,
424 pub common: Common,
425}
426
427#[cfg_attr(
428 feature = "serde",
429 derive(serde::Serialize, serde::Deserialize),
430 serde(deny_unknown_fields, default)
431)]
432#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
433#[derive(Clone, Debug, Default)]
434pub struct Column {
435 pub children: Vec<Element>,
436 pub gap: f32,
437 pub align: Align,
438 pub common: Common,
439}
440
441macro_rules! container_impl {
442 ($ty:ident) => {
443 impl $ty {
444 pub fn new() -> Self {
445 Self::default()
446 }
447
448 pub fn child(mut self, child: impl Into<Element>) -> Self {
449 self.children.push(child.into());
450 self
451 }
452
453 pub fn children(mut self, children: impl IntoIterator<Item = impl Into<Element>>) -> Self {
454 self.children.extend(children.into_iter().map(Into::into));
455 self
456 }
457
458 pub fn gap(mut self, gap: f32) -> Self {
459 self.gap = gap;
460 self
461 }
462
463 pub fn align(mut self, align: Align) -> Self {
464 self.align = align;
465 self
466 }
467
468 common_builder_methods!();
469 }
470 };
471}
472
473container_impl!(Row);
474container_impl!(Column);
475
476#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize), serde(deny_unknown_fields))]
481#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
482#[derive(Clone, Copy, Debug)]
483pub struct Spacer {
484 pub size: f32,
485}
486
487impl Spacer {
488 pub fn new(size: f32) -> Self {
489 Spacer { size }
490 }
491}
492
493#[cfg_attr(
498 feature = "serde",
499 derive(serde::Serialize, serde::Deserialize),
500 serde(deny_unknown_fields, default)
501)]
502#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
503#[derive(Clone, Debug)]
504pub struct Line {
505 pub thickness: f32,
506 pub color: Color,
507 pub common: Common,
508}
509
510impl Default for Line {
511 fn default() -> Self {
512 Line {
513 thickness: 1.0,
514 color: Color::BLACK,
515 common: Common::default(),
516 }
517 }
518}
519
520impl Line {
521 pub fn new() -> Self {
522 Self::default()
523 }
524
525 pub fn thickness(mut self, thickness: f32) -> Self {
526 self.thickness = thickness;
527 self
528 }
529
530 pub fn color(mut self, color: Color) -> Self {
531 self.color = color;
532 self
533 }
534
535 common_builder_methods!();
536}
537
538#[cfg_attr(
543 feature = "serde",
544 derive(serde::Serialize, serde::Deserialize),
545 serde(deny_unknown_fields, default)
546)]
547#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
548#[derive(Clone, Debug, Default)]
549pub struct Rect {
550 pub common: Common,
551}
552
553impl Rect {
554 pub fn new() -> Self {
555 Self::default()
556 }
557
558 common_builder_methods!();
559}
560
561#[cfg_attr(
574 feature = "serde",
575 derive(serde::Serialize, serde::Deserialize),
576 serde(deny_unknown_fields, default)
577)]
578#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
579#[derive(Clone, Debug)]
580pub struct TableOfContents {
581 pub max_depth: u8,
584 pub style: TextStyle,
585 pub leader: char,
588 #[cfg_attr(feature = "serde", serde(skip))]
593 pub skip: usize,
594 pub common: Common,
595}
596
597impl Default for TableOfContents {
598 fn default() -> Self {
599 TableOfContents {
600 max_depth: 3,
601 style: TextStyle::default(),
602 leader: '.',
603 skip: 0,
604 common: Common::default(),
605 }
606 }
607}
608
609impl TableOfContents {
610 pub fn new() -> Self {
611 Self::default()
612 }
613
614 pub fn max_depth(mut self, depth: u8) -> Self {
615 self.max_depth = depth;
616 self
617 }
618
619 pub fn leader(mut self, leader: char) -> Self {
620 self.leader = leader;
621 self
622 }
623
624 pub fn style(mut self, style: TextStyle) -> Self {
625 self.style = style;
626 self
627 }
628
629 common_builder_methods!();
630}
631
632macro_rules! element_from {
637 ($ty:ident) => {
638 impl From<$ty> for Element {
639 fn from(value: $ty) -> Self {
640 Element::$ty(value)
641 }
642 }
643 };
644}
645
646element_from!(Text);
647element_from!(Row);
648element_from!(Column);
649element_from!(Spacer);
650element_from!(Line);
651element_from!(Rect);
652element_from!(Table);
653element_from!(Image);
654element_from!(List);
655element_from!(TableOfContents);
656
657impl From<&str> for Element {
658 fn from(value: &str) -> Self {
659 Element::Text(Text::new(value))
660 }
661}
662
663impl From<String> for Element {
664 fn from(value: String) -> Self {
665 Element::Text(Text::new(value))
666 }
667}