1use std::collections::BTreeMap;
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
21#[serde(rename_all = "camelCase", default)]
22pub struct Report {
23 pub name: String,
25 pub page: PageSettings,
27 pub culture: String,
29 pub data_sources: Vec<DataSource>,
32 pub data_source: Option<String>,
34 pub data_member: Option<String>,
37 pub filter: Option<String>,
39 pub sort: Vec<SortField>,
41 pub parameters: Vec<Parameter>,
43 pub calculated_fields: Vec<CalculatedField>,
45 pub styles: BTreeMap<String, Style>,
47 pub report_header: Option<Band>,
49 pub page_header: Option<Band>,
51 pub groups: Vec<Group>,
53 pub detail: Band,
55 pub detail_reports: Vec<DetailReport>,
57 pub report_footer: Option<Band>,
59 pub page_footer: Option<Band>,
61}
62
63#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
65#[serde(rename_all = "camelCase", default)]
66pub struct DataSource {
67 pub name: String,
69 #[serde(flatten)]
71 pub kind: DataSourceKind,
72}
73
74#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
76#[serde(tag = "type", rename_all = "camelCase")]
77#[non_exhaustive]
78pub enum DataSourceKind {
79 Json {
81 data: serde_json::Value,
83 },
84 Url {
86 url: String,
88 #[serde(default)]
90 headers: BTreeMap<String, String>,
91 },
92 Sql {
94 connection: String,
96 query: String,
98 },
99}
100
101impl Default for DataSourceKind {
102 fn default() -> Self {
103 Self::Json { data: serde_json::Value::Null }
104 }
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
109#[serde(rename_all = "camelCase", default)]
110pub struct PageSettings {
111 pub width: f32,
113 pub height: f32,
115 pub margin_top: f32,
117 pub margin_right: f32,
119 pub margin_bottom: f32,
121 pub margin_left: f32,
123}
124
125impl Default for PageSettings {
126 fn default() -> Self {
127 Self::a4()
128 }
129}
130
131impl PageSettings {
132 pub fn a4() -> Self {
134 Self {
135 width: 595.28,
136 height: 841.89,
137 margin_top: 42.52,
138 margin_right: 42.52,
139 margin_bottom: 42.52,
140 margin_left: 42.52,
141 }
142 }
143
144 pub fn letter() -> Self {
146 Self {
147 width: 612.0,
148 height: 792.0,
149 margin_top: 36.0,
150 margin_right: 36.0,
151 margin_bottom: 36.0,
152 margin_left: 36.0,
153 }
154 }
155
156 pub fn landscape(self) -> Self {
158 Self { width: self.height.max(self.width), height: self.width.min(self.height), ..self }
159 }
160
161 pub fn margins(self, m: f32) -> Self {
163 Self { margin_top: m, margin_right: m, margin_bottom: m, margin_left: m, ..self }
164 }
165
166 pub fn content_width(&self) -> f32 {
168 (self.width - self.margin_left - self.margin_right).max(0.0)
169 }
170}
171
172#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
174#[serde(rename_all = "camelCase", default)]
175pub struct SortField {
176 pub expression: String,
178 pub descending: bool,
180}
181
182#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
184#[serde(rename_all = "camelCase")]
185#[non_exhaustive]
186pub enum ParameterKind {
187 #[default]
189 Text,
190 Number,
192 Date,
194 Boolean,
196}
197
198#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
200#[serde(rename_all = "camelCase", default)]
201pub struct Parameter {
202 pub name: String,
204 pub label: Option<String>,
206 pub kind: ParameterKind,
208 pub default: Option<serde_json::Value>,
210}
211
212#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
214#[serde(rename_all = "camelCase", default)]
215pub struct CalculatedField {
216 pub name: String,
218 pub expression: String,
220}
221
222#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
224#[serde(rename_all = "camelCase", default)]
225pub struct Group {
226 pub expression: String,
228 pub descending: bool,
230 pub header: Option<Band>,
232 pub footer: Option<Band>,
234 pub repeat_header: bool,
236 pub page_break_before: bool,
238}
239
240#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
242#[serde(rename_all = "camelCase", default)]
243pub struct DetailReport {
244 pub data_member: String,
246 pub filter: Option<String>,
248 pub sort: Vec<SortField>,
250 pub header: Option<Band>,
252 pub detail: Band,
254 pub footer: Option<Band>,
256}
257
258#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
260#[serde(rename_all = "camelCase", default)]
261pub struct Band {
262 pub height: f32,
264 pub controls: Vec<Control>,
266 pub visible: Option<String>,
268 pub page_break_before: bool,
270 pub page_break_after: bool,
272 pub keep_with_next: bool,
274 pub odd_style: Option<String>,
276 pub even_style: Option<String>,
278}
279
280impl Default for Band {
281 fn default() -> Self {
282 Self::new(0.0)
283 }
284}
285
286#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
288#[serde(rename_all = "camelCase", default)]
289pub struct Control {
290 #[serde(flatten)]
292 pub kind: ControlKind,
293 pub name: Option<String>,
295 pub x: f32,
297 pub y: f32,
299 pub width: f32,
301 pub height: f32,
303 pub style_name: Option<String>,
305 pub style: Style,
307 pub visible: Option<String>,
309 pub rules: Vec<FormattingRule>,
311}
312
313impl Default for Control {
314 fn default() -> Self {
315 Self {
316 kind: ControlKind::Label(Label::default()),
317 name: None,
318 x: 0.0,
319 y: 0.0,
320 width: 100.0,
321 height: 18.0,
322 style_name: None,
323 style: Style::default(),
324 visible: None,
325 rules: Vec::new(),
326 }
327 }
328}
329
330#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
332#[serde(tag = "type", rename_all = "camelCase")]
333#[non_exhaustive]
334pub enum ControlKind {
335 Label(Label),
337 CheckBox(CheckBox),
339 Line(Line),
341 Shape(Shape),
343 Table(Table),
345 Panel(Panel),
347 Picture(Picture),
349 Link(LinkLabel),
351 Bookmark(Bookmark),
353 Barcode(Barcode),
355 Chart(Chart),
357}
358
359#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
361#[serde(rename_all = "camelCase", default)]
362pub struct Chart {
363 pub kind: crate::chart::ChartKind,
365 pub title: String,
367 pub argument: Option<String>,
370 pub series: Vec<ChartSeries>,
372 pub sorted: bool,
374 pub hide_legend: bool,
376 pub format: Option<String>,
378 pub data_member: Option<String>,
380}
381
382#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
384#[serde(rename_all = "camelCase", default)]
385pub struct ChartSeries {
386 pub name: String,
388 pub value: String,
390 pub color: Option<String>,
392}
393
394impl ChartSeries {
395 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
397 Self { name: name.into(), value: value.into(), color: None }
398 }
399}
400
401#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
403#[serde(rename_all = "camelCase", default)]
404pub struct Barcode {
405 pub symbology: crate::barcode::Symbology,
407 pub text: String,
409 pub expression: Option<String>,
411 pub show_text: bool,
413}
414
415impl Default for Barcode {
416 fn default() -> Self {
417 Self {
418 symbology: crate::barcode::Symbology::default(),
419 text: String::new(),
420 expression: None,
421 show_text: true,
422 }
423 }
424}
425
426#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
428#[serde(rename_all = "camelCase", default)]
429pub struct Picture {
430 pub source: String,
432 pub expression: Option<String>,
434 pub mode: PictureMode,
436}
437
438#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
440#[serde(rename_all = "camelCase")]
441#[non_exhaustive]
442pub enum PictureMode {
443 #[default]
445 Zoom,
446 Squeeze,
448 Stretch,
450 Normal,
452}
453
454#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
456#[serde(rename_all = "camelCase", default)]
457pub struct LinkLabel {
458 #[serde(flatten)]
460 pub label: Label,
461 pub url: String,
463 pub url_expression: Option<String>,
465}
466
467#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
469#[serde(rename_all = "camelCase", default)]
470pub struct Bookmark {
471 pub text: String,
473 pub expression: Option<String>,
475 pub level: u32,
477}
478
479#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
481#[serde(rename_all = "camelCase", default)]
482pub struct Label {
483 pub text: String,
485 pub expression: Option<String>,
487 pub format: Option<String>,
489 pub can_grow: Option<bool>,
491}
492
493impl Label {
494 pub fn grows(&self) -> bool {
496 self.can_grow.unwrap_or(true)
497 }
498}
499
500#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
502#[serde(rename_all = "camelCase", default)]
503pub struct CheckBox {
504 pub expression: String,
506 pub text: String,
508}
509
510#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
512#[serde(rename_all = "camelCase")]
513#[non_exhaustive]
514pub enum LineDirection {
515 #[default]
517 Horizontal,
518 Vertical,
520 Slant,
522}
523
524#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
526#[serde(rename_all = "camelCase", default)]
527pub struct Line {
528 pub direction: LineDirection,
530 pub thickness: Option<f32>,
532}
533
534#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
536#[serde(rename_all = "camelCase")]
537#[non_exhaustive]
538pub enum ShapeKind {
539 #[default]
541 Rectangle,
542 Ellipse,
544}
545
546#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
548#[serde(rename_all = "camelCase", default)]
549pub struct Shape {
550 pub shape: ShapeKind,
552 pub radius: f32,
554}
555
556#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
558#[serde(rename_all = "camelCase", default)]
559pub struct Table {
560 pub rows: Vec<TableRow>,
562}
563
564#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
566#[serde(rename_all = "camelCase", default)]
567pub struct TableRow {
568 pub height: f32,
570 pub cells: Vec<TableCell>,
572}
573
574#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
576#[serde(rename_all = "camelCase", default)]
577pub struct TableCell {
578 pub width: f32,
580 #[serde(flatten)]
582 pub label: Label,
583 pub style_name: Option<String>,
585 pub style: Style,
587}
588
589#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
591#[serde(rename_all = "camelCase", default)]
592pub struct Panel {
593 pub controls: Vec<Control>,
595}
596
597#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
599#[serde(rename_all = "camelCase", default)]
600pub struct FormattingRule {
601 pub condition: String,
603 pub style: Style,
605}
606
607#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
609#[serde(rename_all = "camelCase")]
610#[non_exhaustive]
611pub enum TextAlign {
612 #[default]
614 Left,
615 Center,
617 Right,
619}
620
621#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
623#[serde(rename_all = "camelCase")]
624#[non_exhaustive]
625pub enum VerticalAlign {
626 Top,
628 #[default]
630 Middle,
631 Bottom,
633}
634
635#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
639#[serde(rename_all = "camelCase", default)]
640pub struct Style {
641 pub font_size: Option<f32>,
643 pub bold: Option<bool>,
645 pub color: Option<String>,
647 pub background: Option<String>,
649 pub border_color: Option<String>,
651 pub border_width: Option<f32>,
653 pub borders: Option<String>,
655 pub padding: Option<f32>,
657 pub text_align: Option<TextAlign>,
659 pub vertical_align: Option<VerticalAlign>,
661 pub word_wrap: Option<bool>,
663}
664
665impl Style {
666 pub fn new() -> Self {
668 Self::default()
669 }
670
671 pub fn font_size(mut self, size: f32) -> Self {
673 self.font_size = Some(size);
674 self
675 }
676
677 pub fn bold(mut self) -> Self {
679 self.bold = Some(true);
680 self
681 }
682
683 pub fn color(mut self, color: &str) -> Self {
685 self.color = Some(color.to_owned());
686 self
687 }
688
689 pub fn background(mut self, color: &str) -> Self {
691 self.background = Some(color.to_owned());
692 self
693 }
694
695 pub fn border(mut self, sides: &str, width: f32, color: &str) -> Self {
697 self.borders = Some(sides.to_owned());
698 self.border_width = Some(width);
699 self.border_color = Some(color.to_owned());
700 self
701 }
702
703 pub fn padding(mut self, padding: f32) -> Self {
705 self.padding = Some(padding);
706 self
707 }
708
709 pub fn align(mut self, align: TextAlign) -> Self {
711 self.text_align = Some(align);
712 self
713 }
714
715 pub fn valign(mut self, align: VerticalAlign) -> Self {
717 self.vertical_align = Some(align);
718 self
719 }
720
721 pub fn merged(&self, other: &Style) -> Style {
723 Style {
724 font_size: other.font_size.or(self.font_size),
725 bold: other.bold.or(self.bold),
726 color: other.color.clone().or_else(|| self.color.clone()),
727 background: other.background.clone().or_else(|| self.background.clone()),
728 border_color: other.border_color.clone().or_else(|| self.border_color.clone()),
729 border_width: other.border_width.or(self.border_width),
730 borders: other.borders.clone().or_else(|| self.borders.clone()),
731 padding: other.padding.or(self.padding),
732 text_align: other.text_align.or(self.text_align),
733 vertical_align: other.vertical_align.or(self.vertical_align),
734 word_wrap: other.word_wrap.or(self.word_wrap),
735 }
736 }
737}
738
739impl Report {
740 pub fn new(name: impl Into<String>) -> Self {
742 Self { name: name.into(), culture: "en-US".into(), ..Self::default() }
743 }
744
745 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
747 serde_json::from_str(json)
748 }
749
750 pub fn to_json(&self) -> String {
752 serde_json::to_string_pretty(self).unwrap_or_default()
753 }
754
755 pub fn page(mut self, page: PageSettings) -> Self {
757 self.page = page;
758 self
759 }
760
761 pub fn data_source(mut self, source: DataSource) -> Self {
763 self.data_source = Some(source.name.clone());
764 self.data_sources.push(source);
765 self
766 }
767
768 pub fn embedded_data(&self) -> Option<&serde_json::Value> {
770 let source = match &self.data_source {
771 Some(name) => self.data_sources.iter().find(|s| &s.name == name),
772 None => self.data_sources.first(),
773 }?;
774 match &source.kind {
775 DataSourceKind::Json { data } => Some(data),
776 _ => None,
777 }
778 }
779
780 pub fn culture(mut self, culture: &str) -> Self {
782 self.culture = culture.to_owned();
783 self
784 }
785
786 pub fn data_member(mut self, path: &str) -> Self {
788 self.data_member = Some(path.to_owned());
789 self
790 }
791
792 pub fn filter(mut self, expression: &str) -> Self {
794 self.filter = Some(expression.to_owned());
795 self
796 }
797
798 pub fn sort_by(mut self, expression: &str, descending: bool) -> Self {
800 self.sort.push(SortField { expression: expression.to_owned(), descending });
801 self
802 }
803
804 pub fn parameter(mut self, name: &str, kind: ParameterKind, default: serde_json::Value) -> Self {
806 self.parameters.push(Parameter { name: name.to_owned(), label: None, kind, default: Some(default) });
807 self
808 }
809
810 pub fn parameter_label(mut self, label: &str) -> Self {
812 if let Some(p) = self.parameters.last_mut() {
813 p.label = Some(label.to_owned());
814 }
815 self
816 }
817
818 pub fn calculated(mut self, name: &str, expression: &str) -> Self {
820 self.calculated_fields
821 .push(CalculatedField { name: name.to_owned(), expression: expression.to_owned() });
822 self
823 }
824
825 pub fn style(mut self, name: &str, style: Style) -> Self {
827 self.styles.insert(name.to_owned(), style);
828 self
829 }
830
831 pub fn report_header(mut self, band: Band) -> Self {
833 self.report_header = Some(band);
834 self
835 }
836
837 pub fn page_header(mut self, band: Band) -> Self {
839 self.page_header = Some(band);
840 self
841 }
842
843 pub fn group(mut self, group: Group) -> Self {
845 self.groups.push(group);
846 self
847 }
848
849 pub fn detail(mut self, band: Band) -> Self {
851 self.detail = band;
852 self
853 }
854
855 pub fn detail_report(mut self, detail: DetailReport) -> Self {
857 self.detail_reports.push(detail);
858 self
859 }
860
861 pub fn report_footer(mut self, band: Band) -> Self {
863 self.report_footer = Some(band);
864 self
865 }
866
867 pub fn page_footer(mut self, band: Band) -> Self {
869 self.page_footer = Some(band);
870 self
871 }
872}
873
874impl Group {
875 pub fn new(expression: &str) -> Self {
877 Self { expression: expression.to_owned(), ..Self::default() }
878 }
879
880 pub fn header(mut self, band: Band) -> Self {
882 self.header = Some(band);
883 self
884 }
885
886 pub fn footer(mut self, band: Band) -> Self {
888 self.footer = Some(band);
889 self
890 }
891
892 pub fn repeat_header(mut self) -> Self {
894 self.repeat_header = true;
895 self
896 }
897
898 pub fn page_break_before(mut self) -> Self {
900 self.page_break_before = true;
901 self
902 }
903}
904
905impl DetailReport {
906 pub fn new(data_member: &str) -> Self {
908 Self { data_member: data_member.to_owned(), ..Self::default() }
909 }
910
911 pub fn header(mut self, band: Band) -> Self {
913 self.header = Some(band);
914 self
915 }
916
917 pub fn detail(mut self, band: Band) -> Self {
919 self.detail = band;
920 self
921 }
922
923 pub fn footer(mut self, band: Band) -> Self {
925 self.footer = Some(band);
926 self
927 }
928}
929
930impl Band {
931 pub fn new(height: f32) -> Self {
933 Self {
934 height,
935 controls: Vec::new(),
936 visible: None,
937 page_break_before: false,
938 page_break_after: false,
939 keep_with_next: false,
940 odd_style: None,
941 even_style: None,
942 }
943 }
944
945 pub fn control(mut self, control: Control) -> Self {
947 self.controls.push(control);
948 self
949 }
950
951 pub fn keep_with_next(mut self) -> Self {
953 self.keep_with_next = true;
954 self
955 }
956
957 pub fn zebra(mut self, odd: &str, even: &str) -> Self {
959 self.odd_style = Some(odd.to_owned());
960 self.even_style = Some(even.to_owned());
961 self
962 }
963}
964
965impl Control {
966 pub fn label(text: impl Into<String>) -> Self {
968 Self { kind: ControlKind::Label(Label { text: text.into(), ..Label::default() }), ..Self::default() }
969 }
970
971 pub fn chart(
973 kind: crate::chart::ChartKind,
974 argument: impl Into<String>,
975 value: impl Into<String>,
976 ) -> Self {
977 Self {
978 kind: ControlKind::Chart(Chart {
979 kind,
980 argument: Some(argument.into()),
981 series: vec![ChartSeries::new("", value)],
982 ..Chart::default()
983 }),
984 ..Self::default()
985 }
986 }
987
988 pub fn barcode(symbology: crate::barcode::Symbology, expression: impl Into<String>) -> Self {
990 Self {
991 kind: ControlKind::Barcode(Barcode {
992 symbology,
993 expression: Some(expression.into()),
994 ..Barcode::default()
995 }),
996 ..Self::default()
997 }
998 }
999
1000 pub fn picture(source: impl Into<String>) -> Self {
1002 Self {
1003 kind: ControlKind::Picture(Picture { source: source.into(), ..Picture::default() }),
1004 ..Self::default()
1005 }
1006 }
1007
1008 pub fn link(text: impl Into<String>, url: impl Into<String>) -> Self {
1010 Self {
1011 kind: ControlKind::Link(LinkLabel {
1012 label: Label { text: text.into(), ..Label::default() },
1013 url: url.into(),
1014 url_expression: None,
1015 }),
1016 ..Self::default()
1017 }
1018 }
1019
1020 pub fn bookmark(expression: impl Into<String>, level: u32) -> Self {
1022 Self {
1023 kind: ControlKind::Bookmark(Bookmark {
1024 expression: Some(expression.into()),
1025 level,
1026 ..Bookmark::default()
1027 }),
1028 height: 12.0,
1029 ..Self::default()
1030 }
1031 }
1032
1033 pub fn field(expression: impl Into<String>) -> Self {
1035 Self {
1036 kind: ControlKind::Label(Label { expression: Some(expression.into()), ..Label::default() }),
1037 ..Self::default()
1038 }
1039 }
1040
1041 pub fn check_box(expression: &str, text: &str) -> Self {
1043 Self {
1044 kind: ControlKind::CheckBox(CheckBox {
1045 expression: expression.to_owned(),
1046 text: text.to_owned(),
1047 }),
1048 ..Self::default()
1049 }
1050 }
1051
1052 pub fn line() -> Self {
1054 Self { kind: ControlKind::Line(Line::default()), height: 2.0, ..Self::default() }
1055 }
1056
1057 pub fn rect(radius: f32) -> Self {
1059 Self { kind: ControlKind::Shape(Shape { shape: ShapeKind::Rectangle, radius }), ..Self::default() }
1060 }
1061
1062 pub fn table(table: Table) -> Self {
1064 let height = table.rows.iter().map(|r| r.height).sum();
1065 let width = table.rows.first().map_or(0.0, |r| r.cells.iter().map(|c| c.width).sum());
1066 Self { kind: ControlKind::Table(table), width, height, ..Self::default() }
1067 }
1068
1069 pub fn at(mut self, x: f32, y: f32, width: f32, height: f32) -> Self {
1071 (self.x, self.y, self.width, self.height) = (x, y, width, height);
1072 self
1073 }
1074
1075 pub fn format(mut self, format: &str) -> Self {
1077 if let ControlKind::Label(l) = &mut self.kind {
1078 l.format = Some(format.to_owned());
1079 }
1080 self
1081 }
1082
1083 pub fn style_name(mut self, name: &str) -> Self {
1085 self.style_name = Some(name.to_owned());
1086 self
1087 }
1088
1089 pub fn style(mut self, style: Style) -> Self {
1091 self.style = style;
1092 self
1093 }
1094
1095 pub fn visible(mut self, expression: &str) -> Self {
1097 self.visible = Some(expression.to_owned());
1098 self
1099 }
1100
1101 pub fn rule(mut self, condition: &str, style: Style) -> Self {
1103 self.rules.push(FormattingRule { condition: condition.to_owned(), style });
1104 self
1105 }
1106
1107 pub fn named(mut self, name: &str) -> Self {
1109 self.name = Some(name.to_owned());
1110 self
1111 }
1112}
1113
1114impl Table {
1115 pub fn new() -> Self {
1117 Self::default()
1118 }
1119
1120 pub fn row(mut self, row: TableRow) -> Self {
1122 self.rows.push(row);
1123 self
1124 }
1125}
1126
1127impl TableRow {
1128 pub fn new(height: f32) -> Self {
1130 Self { height, cells: Vec::new() }
1131 }
1132
1133 pub fn text(mut self, width: f32, text: &str) -> Self {
1135 self.cells.push(TableCell {
1136 width,
1137 label: Label { text: text.to_owned(), ..Label::default() },
1138 ..TableCell::default()
1139 });
1140 self
1141 }
1142
1143 pub fn field(mut self, width: f32, expression: &str, format: Option<&str>) -> Self {
1145 self.cells.push(TableCell {
1146 width,
1147 label: Label {
1148 expression: Some(expression.to_owned()),
1149 format: format.map(str::to_owned),
1150 ..Label::default()
1151 },
1152 ..TableCell::default()
1153 });
1154 self
1155 }
1156
1157 pub fn cell_style(mut self, style: Style) -> Self {
1159 if let Some(c) = self.cells.last_mut() {
1160 c.style = style;
1161 }
1162 self
1163 }
1164
1165 pub fn style_name(mut self, name: &str) -> Self {
1167 for c in &mut self.cells {
1168 c.style_name = Some(name.to_owned());
1169 }
1170 self
1171 }
1172}