spreadsheet_ods/
value_.rs

1use std::borrow::Cow;
2
3use chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime};
4use get_size2::GetSize;
5use rust_decimal::prelude::FromPrimitive;
6use rust_decimal::prelude::ToPrimitive;
7use rust_decimal::Decimal;
8
9use crate::text::TextTag;
10
11/// Datatypes for the values. Only the discriminants of the Value enum.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, GetSize)]
13#[allow(missing_docs)]
14pub enum ValueType {
15    Empty,
16    Boolean,
17    Number,
18    Percentage,
19    Currency,
20    Text,
21    TextXml,
22    DateTime,
23    TimeDuration,
24}
25
26/// Content-Values
27#[derive(Debug, Clone, PartialEq, Default)]
28#[allow(missing_docs)]
29pub enum Value {
30    #[default]
31    Empty,
32    Boolean(bool),
33    Number(f64),
34    Percentage(f64),
35    Currency(f64, Box<str>),
36    Text(String),
37    TextXml(Vec<TextTag>),
38    DateTime(NaiveDateTime),
39    TimeDuration(Duration),
40}
41
42impl GetSize for Value {
43    fn get_heap_size(&self) -> usize {
44        match self {
45            Value::Empty => 0,
46            Value::Boolean(_) => 0,
47            Value::Number(_) => 0,
48            Value::Percentage(_) => 0,
49            Value::Currency(_, v) => v.get_heap_size(),
50            Value::Text(v) => v.get_heap_size(),
51            Value::TextXml(v) => v.get_heap_size(),
52            Value::DateTime(_) => 0,
53            Value::TimeDuration(_) => 0,
54        }
55    }
56}
57
58impl Value {
59    /// Return the plan ValueType for this value.
60    pub fn value_type(&self) -> ValueType {
61        match self {
62            Value::Empty => ValueType::Empty,
63            Value::Boolean(_) => ValueType::Boolean,
64            Value::Number(_) => ValueType::Number,
65            Value::Percentage(_) => ValueType::Percentage,
66            Value::Currency(_, _) => ValueType::Currency,
67            Value::Text(_) => ValueType::Text,
68            Value::TextXml(_) => ValueType::TextXml,
69            Value::TimeDuration(_) => ValueType::TimeDuration,
70            Value::DateTime(_) => ValueType::DateTime,
71        }
72    }
73
74    /// Return the bool if the value is a Boolean. Default otherwise.
75    pub fn as_bool_or(&self, d: bool) -> bool {
76        match self {
77            Value::Boolean(b) => *b,
78            _ => d,
79        }
80    }
81
82    /// Return the content as i64 if the value is a number, percentage or
83    /// currency. Default otherwise.
84    pub fn as_i64_or_default(&self) -> i64 {
85        self.as_i64_opt().unwrap_or_default()
86    }
87
88    /// Return the content as i64 if the value is a number, percentage or
89    /// currency. Default otherwise.
90    pub fn as_i64_or(&self, d: i64) -> i64 {
91        self.as_i64_opt().unwrap_or(d)
92    }
93
94    /// Return the content as i64 if the value is a number, percentage or
95    /// currency.
96    pub fn as_i64_opt(&self) -> Option<i64> {
97        match self {
98            Value::Number(n) => Some(*n as i64),
99            Value::Percentage(p) => Some(*p as i64),
100            Value::Currency(v, _) => Some(*v as i64),
101            _ => None,
102        }
103    }
104
105    /// Return the content as u64 if the value is a number, percentage or
106    /// currency. Default otherwise.
107    pub fn as_u64_or_default(&self) -> u64 {
108        self.as_u64_opt().unwrap_or_default()
109    }
110
111    /// Return the content as u64 if the value is a number, percentage or
112    /// currency. Default otherwise.
113    pub fn as_u64_or(&self, d: u64) -> u64 {
114        self.as_u64_opt().unwrap_or(d)
115    }
116
117    /// Return the content as u64 if the value is a number, percentage or
118    /// currency.
119    pub fn as_u64_opt(&self) -> Option<u64> {
120        match self {
121            Value::Number(n) => Some(*n as u64),
122            Value::Percentage(p) => Some(*p as u64),
123            Value::Currency(v, _) => Some(*v as u64),
124            _ => None,
125        }
126    }
127
128    /// Return the content as i32 if the value is a number, percentage or
129    /// currency. Default otherwise.
130    pub fn as_i32_or_default(&self) -> i32 {
131        self.as_i32_opt().unwrap_or_default()
132    }
133
134    /// Return the content as i32 if the value is a number, percentage or
135    /// currency. Default otherwise.
136    pub fn as_i32_or(&self, d: i32) -> i32 {
137        self.as_i32_opt().unwrap_or(d)
138    }
139
140    /// Return the content as i32 if the value is a number, percentage or
141    /// currency.
142    pub fn as_i32_opt(&self) -> Option<i32> {
143        match self {
144            Value::Number(n) => Some(*n as i32),
145            Value::Percentage(p) => Some(*p as i32),
146            Value::Currency(v, _) => Some(*v as i32),
147            _ => None,
148        }
149    }
150
151    /// Return the content as u32 if the value is a number, percentage or
152    /// currency. Default otherwise.
153    pub fn as_u32_or_default(&self) -> u32 {
154        self.as_u32_opt().unwrap_or_default()
155    }
156
157    /// Return the content as u32 if the value is a number, percentage or
158    /// currency. Default otherwise.
159    pub fn as_u32_or(&self, d: u32) -> u32 {
160        self.as_u32_opt().unwrap_or(d)
161    }
162
163    /// Return the content as u32 if the value is a number, percentage or
164    /// currency.
165    pub fn as_u32_opt(&self) -> Option<u32> {
166        match self {
167            Value::Number(n) => Some(*n as u32),
168            Value::Percentage(p) => Some(*p as u32),
169            Value::Currency(v, _) => Some(*v as u32),
170            _ => None,
171        }
172    }
173
174    /// Return the content as i16 if the value is a number, percentage or
175    /// currency. Default otherwise.
176    pub fn as_i16_or_default(&self) -> i16 {
177        self.as_i16_opt().unwrap_or_default()
178    }
179
180    /// Return the content as i16 if the value is a number, percentage or
181    /// currency. Default otherwise.
182    pub fn as_i16_or(&self, d: i16) -> i16 {
183        self.as_i16_opt().unwrap_or(d)
184    }
185
186    /// Return the content as i16 if the value is a number, percentage or
187    /// currency.
188    pub fn as_i16_opt(&self) -> Option<i16> {
189        match self {
190            Value::Number(n) => Some(*n as i16),
191            Value::Percentage(p) => Some(*p as i16),
192            Value::Currency(v, _) => Some(*v as i16),
193            _ => None,
194        }
195    }
196
197    /// Return the content as u16 if the value is a number, percentage or
198    /// currency. Default otherwise.
199    pub fn as_u16_or_default(&self) -> u16 {
200        self.as_u16_opt().unwrap_or_default()
201    }
202
203    /// Return the content as u16 if the value is a number, percentage or
204    /// currency. Default otherwise.
205    pub fn as_u16_or(&self, d: u16) -> u16 {
206        self.as_u16_opt().unwrap_or(d)
207    }
208
209    /// Return the content as u16 if the value is a number, percentage or
210    /// currency.
211    pub fn as_u16_opt(&self) -> Option<u16> {
212        match self {
213            Value::Number(n) => Some(*n as u16),
214            Value::Percentage(p) => Some(*p as u16),
215            Value::Currency(v, _) => Some(*v as u16),
216            _ => None,
217        }
218    }
219
220    /// Return the content as i8 if the value is a number, percentage or
221    /// currency. Default otherwise.
222    pub fn as_i8_or_default(&self) -> i8 {
223        self.as_i8_opt().unwrap_or_default()
224    }
225
226    /// Return the content as i8 if the value is a number, percentage or
227    /// currency. Default otherwise.
228    pub fn as_i8_or(&self, d: i8) -> i8 {
229        self.as_i8_opt().unwrap_or(d)
230    }
231
232    /// Return the content as i8 if the value is a number, percentage or
233    /// currency.
234    pub fn as_i8_opt(&self) -> Option<i8> {
235        match self {
236            Value::Number(n) => Some(*n as i8),
237            Value::Percentage(p) => Some(*p as i8),
238            Value::Currency(v, _) => Some(*v as i8),
239            _ => None,
240        }
241    }
242
243    /// Return the content as u8 if the value is a number, percentage or
244    /// currency. Default otherwise.
245    pub fn as_u8_or_default(&self) -> u8 {
246        self.as_u8_opt().unwrap_or_default()
247    }
248
249    /// Return the content as u8 if the value is a number, percentage or
250    /// currency. Default otherwise.
251    pub fn as_u8_or(&self, d: u8) -> u8 {
252        self.as_u8_opt().unwrap_or(d)
253    }
254
255    /// Return the content as u8 if the value is a number, percentage or
256    /// currency.
257    pub fn as_u8_opt(&self) -> Option<u8> {
258        match self {
259            Value::Number(n) => Some(*n as u8),
260            Value::Percentage(p) => Some(*p as u8),
261            Value::Currency(v, _) => Some(*v as u8),
262            _ => None,
263        }
264    }
265
266    /// Return the content as decimal if the value is a number, percentage or
267    /// currency. Default otherwise.
268    #[cfg(feature = "rust_decimal")]
269    pub fn as_decimal_or_default(&self) -> Decimal {
270        self.as_decimal_opt().unwrap_or_default()
271    }
272
273    /// Return the content as decimal if the value is a number, percentage or
274    /// currency. Default otherwise.
275    #[cfg(feature = "rust_decimal")]
276    pub fn as_decimal_or(&self, d: Decimal) -> Decimal {
277        self.as_decimal_opt().unwrap_or(d)
278    }
279
280    /// Return the content as decimal if the value is a number, percentage or
281    /// currency. Default otherwise.
282    #[cfg(feature = "rust_decimal")]
283    pub fn as_decimal_opt(&self) -> Option<Decimal> {
284        match self {
285            Value::Number(n) => Decimal::from_f64(*n),
286            Value::Currency(v, _) => Decimal::from_f64(*v),
287            Value::Percentage(p) => Decimal::from_f64(*p),
288            _ => None,
289        }
290    }
291
292    /// Return the content as f64 if the value is a number, percentage or
293    /// currency. Default otherwise.
294    pub fn as_f64_or_default(&self) -> f64 {
295        self.as_f64_opt().unwrap_or_default()
296    }
297
298    /// Return the content as f64 if the value is a number, percentage or
299    /// currency. Default otherwise.
300    pub fn as_f64_or(&self, d: f64) -> f64 {
301        self.as_f64_opt().unwrap_or(d)
302    }
303
304    /// Return the content as f64 if the value is a number, percentage or
305    /// currency.
306    pub fn as_f64_opt(&self) -> Option<f64> {
307        match self {
308            Value::Number(n) => Some(*n),
309            Value::Currency(v, _) => Some(*v),
310            Value::Percentage(p) => Some(*p),
311            _ => None,
312        }
313    }
314
315    /// Return the content as str if the value is text.
316    pub fn as_str_or_default(&self) -> &str {
317        self.as_str_opt().unwrap_or_default()
318    }
319
320    /// Return the content as str if the value is text.
321    pub fn as_str_or<'a>(&'a self, d: &'a str) -> &'a str {
322        self.as_str_opt().unwrap_or(d)
323    }
324
325    /// Return the content as str if the value is text or markup text.
326    /// When the cell contains markup all the markup is removed, but
327    /// line-breaks are kept as \n.
328    pub fn as_cow_str_or<'a>(&'a self, d: &'a str) -> Cow<'a, str> {
329        match self {
330            Value::Text(s) => Cow::from(s),
331            Value::TextXml(v) => {
332                let mut buf = String::new();
333                for t in v {
334                    if !buf.is_empty() {
335                        buf.push('\n');
336                    }
337                    t.extract_text(&mut buf);
338                }
339                Cow::from(buf)
340            }
341            _ => Cow::from(d),
342        }
343    }
344
345    /// Return the content as str if the value is text.
346    pub fn as_str_opt(&self) -> Option<&str> {
347        match self {
348            Value::Text(s) => Some(s.as_ref()),
349            _ => None,
350        }
351    }
352
353    /// Return the content as String if the value is text.
354    pub fn as_string_or_default(&self) -> String {
355        self.as_string_opt().unwrap_or_default()
356    }
357
358    /// Return the content as String if the value is text.
359    pub fn as_string_opt(&self) -> Option<String> {
360        match self {
361            Value::Text(s) => Some(s.clone()),
362            _ => None,
363        }
364    }
365
366    /// Return the content as Duration if the value is a TimeDuration.
367    /// Default otherwise.
368    pub fn as_timeduration_or_default(&self) -> Duration {
369        self.as_timeduration_opt().unwrap_or_default()
370    }
371
372    /// Return the content as Duration if the value is a TimeDuration.
373    /// Default otherwise.
374    pub fn as_timeduration_or(&self, d: Duration) -> Duration {
375        self.as_timeduration_opt().unwrap_or(d)
376    }
377
378    /// Return the content as Duration if the value is a TimeDuration.
379    /// Default otherwise.
380    pub fn as_timeduration_opt(&self) -> Option<Duration> {
381        match self {
382            Value::TimeDuration(td) => Some(*td),
383            _ => None,
384        }
385    }
386
387    /// Return the content as NaiveDateTime if the value is a DateTime.
388    /// Default otherwise.
389    pub fn as_datetime_or_default(&self) -> NaiveDateTime {
390        self.as_datetime_opt().unwrap_or_default()
391    }
392
393    /// Return the content as NaiveDateTime if the value is a DateTime.
394    /// Default otherwise.
395    pub fn as_datetime_or(&self, d: NaiveDateTime) -> NaiveDateTime {
396        self.as_datetime_opt().unwrap_or(d)
397    }
398
399    /// Return the content as an optional NaiveDateTime if the value is
400    /// a DateTime.
401    pub fn as_datetime_opt(&self) -> Option<NaiveDateTime> {
402        match self {
403            Value::DateTime(dt) => Some(*dt),
404            _ => None,
405        }
406    }
407
408    /// Return the content as NaiveDate if the value is a DateTime.
409    /// Default otherwise.
410    pub fn as_date_or_default(&self) -> NaiveDate {
411        self.as_date_opt().unwrap_or_default()
412    }
413
414    /// Return the content as NaiveDate if the value is a DateTime.
415    /// Default otherwise.
416    pub fn as_date_or(&self, d: NaiveDate) -> NaiveDate {
417        self.as_date_opt().unwrap_or(d)
418    }
419
420    /// Return the content as an optional NaiveDateTime if the value is
421    /// a DateTime.
422    pub fn as_date_opt(&self) -> Option<NaiveDate> {
423        match self {
424            Value::DateTime(dt) => Some(dt.date()),
425            _ => None,
426        }
427    }
428
429    /// Returns the currency code or "" if the value is not a currency.
430    pub fn currency(&self) -> &str {
431        match self {
432            Value::Currency(_, c) => c,
433            _ => "",
434        }
435    }
436
437    /// Create a currency value.
438    #[allow(clippy::needless_range_loop)]
439    pub fn new_currency<S: AsRef<str>>(cur: S, value: f64) -> Self {
440        Value::Currency(value, cur.as_ref().into())
441    }
442
443    /// Create a percentage value.
444    pub fn new_percentage(value: f64) -> Self {
445        Value::Percentage(value)
446    }
447}
448
449/// currency value
450#[macro_export]
451macro_rules! currency {
452    ($c:expr, $v:expr) => {
453        Value::new_currency($c, $v as f64)
454    };
455}
456
457/// currency value
458#[macro_export]
459macro_rules! percent {
460    ($v:expr) => {
461        Value::new_percentage($v)
462    };
463}
464
465impl From<()> for Value {
466    fn from(_: ()) -> Self {
467        Value::Empty
468    }
469}
470
471impl From<&str> for Value {
472    fn from(s: &str) -> Self {
473        Value::Text(s.to_string())
474    }
475}
476
477impl From<String> for Value {
478    fn from(s: String) -> Self {
479        Value::Text(s)
480    }
481}
482
483impl From<&String> for Value {
484    fn from(s: &String) -> Self {
485        Value::Text(s.to_string())
486    }
487}
488
489impl From<TextTag> for Value {
490    fn from(t: TextTag) -> Self {
491        Value::TextXml(vec![t])
492    }
493}
494
495impl From<Vec<TextTag>> for Value {
496    fn from(t: Vec<TextTag>) -> Self {
497        Value::TextXml(t)
498    }
499}
500
501impl From<Option<&str>> for Value {
502    fn from(s: Option<&str>) -> Self {
503        if let Some(s) = s {
504            Value::Text(s.to_string())
505        } else {
506            Value::Empty
507        }
508    }
509}
510
511impl From<Option<&String>> for Value {
512    fn from(s: Option<&String>) -> Self {
513        if let Some(s) = s {
514            Value::Text(s.to_string())
515        } else {
516            Value::Empty
517        }
518    }
519}
520
521impl From<Option<String>> for Value {
522    fn from(s: Option<String>) -> Self {
523        if let Some(s) = s {
524            Value::Text(s)
525        } else {
526            Value::Empty
527        }
528    }
529}
530
531#[cfg(feature = "rust_decimal")]
532impl From<Decimal> for Value {
533    fn from(f: Decimal) -> Self {
534        Value::Number(f.to_f64().expect("decimal->f64 should not fail"))
535    }
536}
537
538#[cfg(feature = "rust_decimal")]
539impl From<Option<Decimal>> for Value {
540    fn from(f: Option<Decimal>) -> Self {
541        if let Some(f) = f {
542            Value::Number(f.to_f64().expect("decimal->f64 should not fail"))
543        } else {
544            Value::Empty
545        }
546    }
547}
548
549macro_rules! from_number {
550    ($l:ty) => {
551        impl From<$l> for Value {
552            #![allow(trivial_numeric_casts)]
553            fn from(f: $l) -> Self {
554                Value::Number(f as f64)
555            }
556        }
557
558        impl From<&$l> for Value {
559            #![allow(trivial_numeric_casts)]
560            fn from(f: &$l) -> Self {
561                Value::Number(*f as f64)
562            }
563        }
564
565        impl From<Option<$l>> for Value {
566            #![allow(trivial_numeric_casts)]
567            fn from(f: Option<$l>) -> Self {
568                if let Some(f) = f {
569                    Value::Number(f as f64)
570                } else {
571                    Value::Empty
572                }
573            }
574        }
575
576        impl From<Option<&$l>> for Value {
577            #![allow(trivial_numeric_casts)]
578            fn from(f: Option<&$l>) -> Self {
579                if let Some(f) = f {
580                    Value::Number(*f as f64)
581                } else {
582                    Value::Empty
583                }
584            }
585        }
586    };
587}
588
589from_number!(f64);
590from_number!(f32);
591from_number!(i64);
592from_number!(i32);
593from_number!(i16);
594from_number!(i8);
595from_number!(u64);
596from_number!(u32);
597from_number!(u16);
598from_number!(u8);
599
600impl From<bool> for Value {
601    fn from(b: bool) -> Self {
602        Value::Boolean(b)
603    }
604}
605
606impl From<Option<bool>> for Value {
607    fn from(b: Option<bool>) -> Self {
608        if let Some(b) = b {
609            Value::Boolean(b)
610        } else {
611            Value::Empty
612        }
613    }
614}
615
616impl From<NaiveDateTime> for Value {
617    fn from(dt: NaiveDateTime) -> Self {
618        Value::DateTime(dt)
619    }
620}
621
622impl From<Option<NaiveDateTime>> for Value {
623    fn from(dt: Option<NaiveDateTime>) -> Self {
624        if let Some(dt) = dt {
625            Value::DateTime(dt)
626        } else {
627            Value::Empty
628        }
629    }
630}
631
632impl From<NaiveDate> for Value {
633    fn from(dt: NaiveDate) -> Self {
634        Value::DateTime(dt.and_hms_opt(0, 0, 0).unwrap())
635    }
636}
637
638impl From<Option<NaiveDate>> for Value {
639    fn from(dt: Option<NaiveDate>) -> Self {
640        if let Some(dt) = dt {
641            Value::DateTime(dt.and_hms_opt(0, 0, 0).expect("valid time"))
642        } else {
643            Value::Empty
644        }
645    }
646}
647
648impl From<NaiveTime> for Value {
649    fn from(ti: NaiveTime) -> Self {
650        Value::DateTime(NaiveDateTime::new(
651            NaiveDate::from_ymd_opt(1899, 12, 30).expect("valid date"),
652            ti,
653        ))
654    }
655}
656
657impl From<Option<NaiveTime>> for Value {
658    fn from(dt: Option<NaiveTime>) -> Self {
659        if let Some(ti) = dt {
660            Value::DateTime(NaiveDateTime::new(
661                NaiveDate::from_ymd_opt(1899, 12, 30).expect("valid date"),
662                ti,
663            ))
664        } else {
665            Value::Empty
666        }
667    }
668}
669
670impl From<Duration> for Value {
671    fn from(d: Duration) -> Self {
672        Value::TimeDuration(d)
673    }
674}
675
676impl From<Option<Duration>> for Value {
677    fn from(d: Option<Duration>) -> Self {
678        if let Some(d) = d {
679            Value::TimeDuration(d)
680        } else {
681            Value::Empty
682        }
683    }
684}