umya_spreadsheet/structs/
cell_value.rs

1use super::RichText;
2use super::SharedStringItem;
3use super::Text;
4use crate::helper::formula::*;
5use crate::structs::CellFormula;
6use crate::structs::CellRawValue;
7use crate::traits::AdjustmentCoordinateWith2Sheet;
8use crate::CellErrorType;
9use std::borrow::Cow;
10use std::str::FromStr;
11
12#[derive(Clone, Default, Debug, PartialEq, PartialOrd)]
13pub struct CellValue {
14    pub(crate) raw_value: CellRawValue,
15    pub(crate) formula: Option<Box<CellFormula>>,
16}
17impl CellValue {
18    #[inline]
19    pub fn get_data_type(&self) -> &str {
20        self.raw_value.get_data_type()
21    }
22
23    #[inline]
24    pub fn get_raw_value(&self) -> &CellRawValue {
25        &self.raw_value
26    }
27
28    #[inline]
29    pub(crate) fn get_data_type_crate(&self) -> &str {
30        match &self.formula {
31            Some(_) => "str",
32            None => self.raw_value.get_data_type(),
33        }
34    }
35
36    #[inline]
37    pub fn get_value(&self) -> Cow<'static, str> {
38        self.raw_value.to_string().into()
39    }
40
41    #[inline]
42    pub fn get_value_number(&self) -> Option<f64> {
43        self.raw_value.get_number()
44    }
45
46    #[inline]
47    pub fn get_value_lazy(&mut self) -> Cow<'static, str> {
48        if let CellRawValue::Lazy(v) = &self.raw_value {
49            self.raw_value = Self::guess_typed_data(v);
50        }
51        self.remove_formula();
52        self.raw_value.to_string().into()
53    }
54
55    #[inline]
56    pub(crate) fn get_text(&self) -> Option<Text> {
57        self.raw_value.get_text()
58    }
59
60    #[inline]
61    pub(crate) fn get_rich_text(&self) -> Option<RichText> {
62        self.raw_value.get_rich_text()
63    }
64
65    /// Set the raw value after trying to convert `value` into one of the supported data types.
66    /// <br />
67    /// Types that `value` may be converted to:
68    /// - `Empty` - if the string was `""`
69    /// - `Numeric` - if the string can be parsed to an `f64`
70    /// - `Bool` - if the string was either `"TRUE"` or `"FALSE"`
71    /// - `Error` - if the string was either `"#VALUE!"`,`"#REF!"`,`"#NUM!"`,`"#NULL!"`,`"#NAME?"`,`"#N/A"`,`"#DATA!"` or `"#DIV/0!"`
72    /// - `String` - if the string does not fulfill any of the other conditions
73    #[inline]
74    pub fn set_value<S: Into<String>>(&mut self, value: S) -> &mut Self {
75        self.raw_value = Self::guess_typed_data(&value.into());
76        self.remove_formula();
77        self
78    }
79
80    #[inline]
81    pub(crate) fn set_value_crate<S: Into<String>>(&mut self, value: S) -> &mut Self {
82        self.raw_value = Self::guess_typed_data(&value.into());
83        self
84    }
85
86    #[inline]
87    pub fn set_value_lazy<S: Into<String>>(&mut self, value: S) -> &mut Self {
88        self.raw_value = CellRawValue::Lazy(value.into().into_boxed_str());
89        self
90    }
91
92    #[inline]
93    pub fn set_value_string<S: Into<String>>(&mut self, value: S) -> &mut Self {
94        self.raw_value = CellRawValue::String(value.into().into_boxed_str());
95        self.remove_formula();
96        self
97    }
98
99    #[inline]
100    pub(crate) fn set_value_string_crate<S: Into<String>>(&mut self, value: S) -> &mut Self {
101        self.raw_value = CellRawValue::String(value.into().into_boxed_str());
102        self
103    }
104
105    #[inline]
106    pub fn set_value_bool(&mut self, value: bool) -> &mut Self {
107        self.raw_value = CellRawValue::Bool(value);
108        self.remove_formula();
109        self
110    }
111
112    #[inline]
113    pub(crate) fn set_value_bool_crate(&mut self, value: bool) -> &mut Self {
114        self.raw_value = CellRawValue::Bool(value);
115        self
116    }
117
118    #[inline]
119    pub fn set_value_number<T>(&mut self, value: T) -> &mut Self
120    where
121        T: Into<f64>,
122    {
123        self.raw_value = CellRawValue::Numeric(value.into());
124        self.remove_formula();
125        self
126    }
127
128    #[inline]
129    pub fn set_rich_text(&mut self, value: RichText) -> &mut Self {
130        self.raw_value = CellRawValue::RichText(value);
131        self.remove_formula();
132        self
133    }
134
135    #[inline]
136    pub fn set_blank(&mut self) -> &mut Self {
137        self.raw_value = CellRawValue::Empty;
138        self.remove_formula();
139        self
140    }
141
142    #[inline]
143    pub fn is_formula(&self) -> bool {
144        self.formula.is_some()
145    }
146
147    #[inline]
148    pub fn get_formula(&self) -> &str {
149        match &self.formula {
150            Some(v) => v.get_text(),
151            None => "",
152        }
153    }
154
155    #[inline]
156    pub fn get_formula_obj(&self) -> Option<&CellFormula> {
157        self.formula.as_deref()
158    }
159
160    #[inline]
161    pub fn set_formula<S: Into<String>>(&mut self, value: S) -> &mut Self {
162        let mut obj = CellFormula::default();
163        obj.set_text(value.into());
164        self.formula = Some(Box::new(obj));
165        self
166    }
167
168    #[inline]
169    pub fn set_formula_obj(&mut self, value: CellFormula) -> &mut Self {
170        self.formula = Some(Box::new(value));
171        self
172    }
173
174    #[inline]
175    pub fn remove_formula(&mut self) -> &mut Self {
176        self.formula = None;
177        self
178    }
179
180    #[inline]
181    pub fn set_formula_result_default<S: Into<String>>(&mut self, value: S) -> &mut Self {
182        self.set_value_crate(value);
183        self
184    }
185
186    #[inline]
187    pub fn set_error<S: Into<String>>(&mut self, value: S) -> &mut Self {
188        self.set_value_crate(value);
189        self
190    }
191
192    #[inline]
193    pub fn is_error(&self) -> bool {
194        self.raw_value.is_error()
195    }
196
197    #[inline]
198    pub(crate) fn set_shared_string_item(&mut self, value: SharedStringItem) -> &mut Self {
199        if let Some(v) = value.get_text() {
200            self.set_value_string(v.get_value());
201        }
202        if let Some(v) = value.get_rich_text() {
203            self.set_rich_text(v.clone());
204        }
205        self
206    }
207
208    #[inline]
209    pub(crate) fn guess_typed_data(value: &str) -> CellRawValue {
210        let uppercase_value = value.to_uppercase();
211
212        match uppercase_value.as_str() {
213            "" => CellRawValue::Empty,
214            "TRUE" => CellRawValue::Bool(true),
215            "FALSE" => CellRawValue::Bool(false),
216            "NAN" => CellRawValue::String(value.into()),
217            _ => {
218                if let Ok(error_type) = CellErrorType::from_str(&uppercase_value) {
219                    CellRawValue::Error(error_type)
220                } else if let Ok(f) = value.parse::<f64>() {
221                    CellRawValue::Numeric(f)
222                } else {
223                    CellRawValue::String(value.into())
224                }
225            }
226        }
227    }
228
229    #[inline]
230    pub fn is_empty(&self) -> bool {
231        self.is_value_empty() && self.is_formula_empty()
232    }
233
234    #[inline]
235    pub(crate) fn is_value_empty(&self) -> bool {
236        self.raw_value.is_empty()
237    }
238
239    #[inline]
240    pub(crate) fn is_formula_empty(&self) -> bool {
241        !self.is_formula()
242    }
243
244    // When opened in software such as Excel, it is visually blank.
245    #[inline]
246    pub(crate) fn is_visually_empty(&self) -> bool {
247        self.get_value() == "" && self.is_formula_empty()
248    }
249}
250impl AdjustmentCoordinateWith2Sheet for CellValue {
251    #[inline]
252    fn adjustment_insert_coordinate_with_2sheet(
253        &mut self,
254        self_sheet_name: &str,
255        sheet_name: &str,
256        root_col_num: &u32,
257        offset_col_num: &u32,
258        root_row_num: &u32,
259        offset_row_num: &u32,
260    ) {
261        if let Some(v) = &mut self.formula {
262            v.adjustment_insert_coordinate_with_2sheet(
263                self_sheet_name,
264                sheet_name,
265                root_col_num,
266                offset_col_num,
267                root_row_num,
268                offset_row_num,
269            );
270        }
271    }
272
273    #[inline]
274    fn adjustment_remove_coordinate_with_2sheet(
275        &mut self,
276        self_sheet_name: &str,
277        sheet_name: &str,
278        root_col_num: &u32,
279        offset_col_num: &u32,
280        root_row_num: &u32,
281        offset_row_num: &u32,
282    ) {
283        if let Some(v) = &mut self.formula {
284            v.adjustment_remove_coordinate_with_2sheet(
285                self_sheet_name,
286                sheet_name,
287                root_col_num,
288                offset_col_num,
289                root_row_num,
290                offset_row_num,
291            );
292        }
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::*;
299
300    #[test]
301    fn set_value() {
302        let mut obj = CellValue::default();
303
304        obj.set_value_string(String::from("TEST"));
305        assert_eq!(obj.get_value(), "TEST");
306        assert!(obj.get_value_number().is_none());
307
308        obj.set_value_string("TEST");
309        assert_eq!(obj.get_value(), "TEST");
310
311        obj.set_value_bool(true);
312        assert_eq!(obj.get_value(), "TRUE");
313
314        obj.set_value_number(1);
315        assert_eq!(obj.get_value(), "1");
316
317        obj.set_blank();
318        assert_eq!(obj.get_value(), "");
319
320        obj.set_error("#NUM!");
321        assert_eq!(obj.get_value(), "#NUM!");
322    }
323
324    #[test]
325    fn error_checking() {
326        let path = std::path::Path::new("./tests/test_files/pr_204.xlsx");
327        let book = crate::reader::xlsx::read(path).unwrap();
328        let sheet = book.get_sheet(&0).unwrap();
329
330        let cell = sheet.get_cell_value("A1");
331        assert!(cell.raw_value.is_error());
332        assert_eq!(cell.raw_value, CellRawValue::Error(CellErrorType::Div0));
333
334        let cell = sheet.get_cell_value("A2");
335        assert!(cell.raw_value.is_error());
336        assert_eq!(cell.raw_value, CellRawValue::Error(CellErrorType::Name));
337
338        let cell = sheet.get_cell_value("A3");
339        assert!(cell.raw_value.is_error());
340        assert_eq!(cell.raw_value, CellRawValue::Error(CellErrorType::Ref));
341
342        let cell = sheet.get_cell_value("A4");
343        assert!(cell.raw_value.is_error());
344        assert_eq!(cell.raw_value, CellRawValue::Error(CellErrorType::Value));
345
346        let cell = sheet.get_cell_value("A5");
347        assert!(cell.raw_value.is_error());
348        assert_eq!(cell.raw_value, CellRawValue::Error(CellErrorType::NA));
349
350        let cell = sheet.get_cell_value("A6");
351        assert!(cell.raw_value.is_error());
352        assert_eq!(cell.raw_value, CellRawValue::Error(CellErrorType::Num));
353
354        let cell = sheet.get_cell_value("A7");
355        assert!(cell.raw_value.is_error());
356        assert_eq!(cell.raw_value, CellRawValue::Error(CellErrorType::Null));
357    }
358}