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}