1use serde::{Deserialize, Serialize};
2
3use super::element::ButtonElement;
4use super::rich_text::RichTextBlock;
5use super::tag::{tagged_enum, Tag, Tagged};
6
7#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct TableBlock {
10 r#type: Tag<Self>,
11 #[serde(skip_serializing_if = "Option::is_none")]
12 pub block_id: Option<String>,
13 pub rows: Vec<Vec<TableCell>>,
14 #[serde(default, skip_serializing_if = "Vec::is_empty")]
15 pub column_settings: Vec<Option<ColumnSetting>>,
16}
17
18impl Tagged for TableBlock {
19 const TAG: &'static str = "table";
20}
21
22impl TableBlock {
23 pub fn new(rows: Vec<Vec<TableCell>>) -> Self {
24 Self {
25 r#type: Tag::default(),
26 block_id: None,
27 rows,
28 column_settings: Vec::new(),
29 }
30 }
31
32 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
33 self.block_id = Some(block_id.into());
34 self
35 }
36
37 pub fn column_settings(mut self, column_settings: Vec<Option<ColumnSetting>>) -> Self {
38 self.column_settings = column_settings;
39 self
40 }
41}
42
43tagged_enum! {
44 pub enum TableCell("type") {
46 RawText(RawTextCell),
47 RawNumber(RawNumberCell),
48 RichText(RichTextBlock),
49 }
50}
51
52#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
54pub struct ColumnSetting {
55 #[serde(skip_serializing_if = "Option::is_none")]
56 pub align: Option<ColumnAlign>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub is_wrapped: Option<bool>,
59}
60
61impl ColumnSetting {
62 pub fn new() -> Self {
63 Self::default()
64 }
65
66 pub fn align(mut self, align: ColumnAlign) -> Self {
67 self.align = Some(align);
68 self
69 }
70
71 pub fn is_wrapped(mut self, is_wrapped: bool) -> Self {
72 self.is_wrapped = Some(is_wrapped);
73 self
74 }
75}
76
77#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
79pub struct DataTableBlock {
80 r#type: Tag<Self>,
81 pub rows: Vec<Vec<DataTableCell>>,
82 #[serde(skip_serializing_if = "Option::is_none")]
83 pub block_id: Option<String>,
84 #[serde(skip_serializing_if = "Option::is_none")]
85 pub page_size: Option<u32>,
86 pub caption: String,
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub row_header_column_index: Option<u32>,
89}
90
91impl Tagged for DataTableBlock {
92 const TAG: &'static str = "data_table";
93}
94
95impl DataTableBlock {
96 pub fn new(rows: Vec<Vec<DataTableCell>>, caption: impl Into<String>) -> Self {
97 Self {
98 r#type: Tag::default(),
99 rows,
100 block_id: None,
101 page_size: None,
102 caption: caption.into(),
103 row_header_column_index: None,
104 }
105 }
106
107 pub fn block_id(mut self, block_id: impl Into<String>) -> Self {
108 self.block_id = Some(block_id.into());
109 self
110 }
111
112 pub fn page_size(mut self, page_size: u32) -> Self {
113 self.page_size = Some(page_size);
114 self
115 }
116
117 pub fn row_header_column_index(mut self, row_header_column_index: u32) -> Self {
118 self.row_header_column_index = Some(row_header_column_index);
119 self
120 }
121}
122
123tagged_enum! {
124 pub enum DataTableCell("type") {
126 RawText(RawTextCell),
127 RawNumber(RawNumberCell),
128 RichText(RichTextBlock),
129 ActionCell(ActionCell),
130 }
131}
132
133#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
135pub struct RawTextCell {
136 r#type: Tag<Self>,
137 pub text: String,
138}
139
140impl Tagged for RawTextCell {
141 const TAG: &'static str = "raw_text";
142}
143
144impl RawTextCell {
145 pub fn new(text: impl Into<String>) -> Self {
146 Self {
147 r#type: Tag::default(),
148 text: text.into(),
149 }
150 }
151}
152
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
155pub struct RawNumberCell {
156 r#type: Tag<Self>,
157 pub value: f64,
158 #[serde(skip_serializing_if = "Option::is_none")]
159 pub text: Option<String>,
160}
161
162impl Tagged for RawNumberCell {
163 const TAG: &'static str = "raw_number";
164}
165
166impl RawNumberCell {
167 pub fn new(value: f64) -> Self {
168 Self {
169 r#type: Tag::default(),
170 value,
171 text: None,
172 }
173 }
174
175 pub fn text(mut self, text: impl Into<String>) -> Self {
176 self.text = Some(text.into());
177 self
178 }
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
183pub struct ActionCell {
184 r#type: Tag<Self>,
185 pub element: Box<ButtonElement>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 pub fallback: Option<ActionCellFallback>,
188}
189
190impl Tagged for ActionCell {
191 const TAG: &'static str = "action_cell";
192}
193
194impl ActionCell {
195 pub fn new(element: ButtonElement) -> Self {
196 Self {
197 r#type: Tag::default(),
198 element: Box::new(element),
199 fallback: None,
200 }
201 }
202
203 pub fn fallback(mut self, fallback: impl Into<ActionCellFallback>) -> Self {
204 self.fallback = Some(fallback.into());
205 self
206 }
207}
208
209tagged_enum! {
210 pub enum ActionCellFallback("type") {
212 RawText(RawTextCell),
213 RawNumber(RawNumberCell),
214 }
215}
216
217#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
219#[serde(rename_all = "snake_case")]
220pub enum ColumnAlign {
221 Left,
222 Center,
223 Right,
224 #[serde(untagged)]
225 Other(String),
226}