Skip to main content

teaql_core/
xls.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7pub struct XlsBlock {
8    pub page: String,
9    pub top: i32,
10    pub bottom: i32,
11    pub left: i32,
12    pub right: i32,
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub style_refer_block: Option<Box<XlsBlock>>,
15    #[serde(skip_serializing_if = "serde_json::Value::is_null")]
16    pub value: serde_json::Value,
17    #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
18    pub properties: BTreeMap<String, serde_json::Value>,
19}
20
21impl XlsBlock {
22    pub fn new(
23        page: impl Into<String>,
24        x: i32,
25        y: i32,
26        value: impl Into<serde_json::Value>,
27    ) -> Self {
28        Self {
29            page: page.into(),
30            top: y,
31            bottom: y,
32            left: x,
33            right: x,
34            style_refer_block: None,
35            value: value.into(),
36            properties: BTreeMap::new(),
37        }
38    }
39
40    pub fn from_context(
41        context: &XlsBlockBuildContext,
42        value: impl Into<serde_json::Value>,
43    ) -> Self {
44        Self::new(context.page.clone(), context.x, context.y, value)
45    }
46
47    pub fn region(mut self, left: i32, top: i32, right: i32, bottom: i32) -> Self {
48        self.left = left;
49        self.top = top;
50        self.right = right;
51        self.bottom = bottom;
52        self
53    }
54
55    pub fn span(mut self, width: i32, height: i32) -> Self {
56        self.right = self.left + width.saturating_sub(1);
57        self.bottom = self.top + height.saturating_sub(1);
58        self
59    }
60
61    pub fn value(mut self, value: impl Into<serde_json::Value>) -> Self {
62        self.value = value.into();
63        self
64    }
65
66    pub fn add_property(
67        mut self,
68        name: impl Into<String>,
69        value: impl Into<serde_json::Value>,
70    ) -> Self {
71        self.properties.insert(name.into(), value.into());
72        self
73    }
74
75    pub fn set_property(&mut self, name: impl Into<String>, value: impl Into<serde_json::Value>) {
76        self.properties.insert(name.into(), value.into());
77    }
78
79    pub fn style(mut self, style: XlsBlock) -> Self {
80        self.style_refer_block = Some(Box::new(style));
81        self
82    }
83
84    pub fn width(&self) -> i32 {
85        self.right - self.left + 1
86    }
87
88    pub fn height(&self) -> i32 {
89        self.bottom - self.top + 1
90    }
91
92    pub fn contains(&self, x: i32, y: i32) -> bool {
93        x >= self.left && x <= self.right && y >= self.top && y <= self.bottom
94    }
95
96    pub fn to_json_value(&self) -> serde_json::Value {
97        serde_json::to_value(self).expect("XlsBlock serialization cannot fail")
98    }
99}
100
101#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
102#[serde(rename_all = "camelCase")]
103pub struct XlsBlockBuildContext {
104    pub page: String,
105    pub start_x: i32,
106    pub x: i32,
107    pub y: i32,
108}
109
110impl XlsBlockBuildContext {
111    pub fn new(page: impl Into<String>, x: i32, y: i32) -> Self {
112        let x = x.max(0);
113        let y = y.max(0);
114        Self {
115            page: page.into(),
116            start_x: x,
117            x,
118            y,
119        }
120    }
121
122    pub fn page(page: impl Into<String>) -> Self {
123        Self::new(page, 0, 0)
124    }
125
126    pub fn next(&self) -> Self {
127        Self {
128            page: self.page.clone(),
129            start_x: self.start_x,
130            x: self.x + 1,
131            y: self.y,
132        }
133    }
134
135    pub fn new_line(&self) -> Self {
136        Self {
137            page: self.page.clone(),
138            start_x: self.start_x,
139            x: 0,
140            y: self.y + 1,
141        }
142    }
143
144    pub fn next_line(&self) -> Self {
145        Self {
146            page: self.page.clone(),
147            start_x: self.start_x,
148            x: self.start_x,
149            y: self.y + 1,
150        }
151    }
152
153    pub fn to_block(&self, value: impl Into<serde_json::Value>) -> XlsBlock {
154        XlsBlock::from_context(self, value)
155    }
156}
157
158#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
159#[serde(rename_all = "camelCase")]
160pub struct XlsPage {
161    pub name: String,
162    pub blocks: Vec<XlsBlock>,
163}
164
165impl XlsPage {
166    pub fn new(name: impl Into<String>) -> Self {
167        Self {
168            name: name.into(),
169            blocks: Vec::new(),
170        }
171    }
172
173    pub fn add_block(mut self, block: XlsBlock) -> Self {
174        self.blocks.push(block);
175        self
176    }
177
178    pub fn push_block(&mut self, block: XlsBlock) {
179        self.blocks.push(block);
180    }
181
182    pub fn block_at(&self, x: i32, y: i32) -> Option<&XlsBlock> {
183        self.blocks.iter().find(|block| block.contains(x, y))
184    }
185}
186
187#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
188#[serde(rename_all = "camelCase")]
189pub struct XlsWorkbook {
190    pub pages: Vec<XlsPage>,
191}
192
193impl XlsWorkbook {
194    pub fn new() -> Self {
195        Self::default()
196    }
197
198    pub fn add_page(mut self, page: XlsPage) -> Self {
199        self.pages.push(page);
200        self
201    }
202
203    pub fn push_page(&mut self, page: XlsPage) {
204        self.pages.push(page);
205    }
206
207    pub fn page(&self, name: &str) -> Option<&XlsPage> {
208        self.pages.iter().find(|page| page.name == name)
209    }
210
211    pub fn to_json_value(&self) -> serde_json::Value {
212        serde_json::to_value(self).expect("XlsWorkbook serialization cannot fail")
213    }
214}