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                let mut add_newline = false;
334                for t in v {
335                    if add_newline {
336                        buf.push('\n');
337                    }
338                    t.extract_text(&mut buf);
339                    add_newline = true;
340                }
341                Cow::from(buf)
342            }
343            _ => Cow::from(d),
344        }
345    }
346
347    /// Return the content as str if the value is text.
348    pub fn as_str_opt(&self) -> Option<&str> {
349        match self {
350            Value::Text(s) => Some(s.as_ref()),
351            _ => None,
352        }
353    }
354
355    /// Return the content as String if the value is text.
356    pub fn as_string_or_default(&self) -> String {
357        self.as_string_opt().unwrap_or_default()
358    }
359
360    /// Return the content as String if the value is text.
361    pub fn as_string_opt(&self) -> Option<String> {
362        match self {
363            Value::Text(s) => Some(s.clone()),
364            _ => None,
365        }
366    }
367
368    /// Return the content as Duration if the value is a TimeDuration.
369    /// Default otherwise.
370    pub fn as_timeduration_or_default(&self) -> Duration {
371        self.as_timeduration_opt().unwrap_or_default()
372    }
373
374    /// Return the content as Duration if the value is a TimeDuration.
375    /// Default otherwise.
376    pub fn as_timeduration_or(&self, d: Duration) -> Duration {
377        self.as_timeduration_opt().unwrap_or(d)
378    }
379
380    /// Return the content as Duration if the value is a TimeDuration.
381    /// Default otherwise.
382    pub fn as_timeduration_opt(&self) -> Option<Duration> {
383        match self {
384            Value::TimeDuration(td) => Some(*td),
385            _ => None,
386        }
387    }
388
389    /// Return the content as NaiveDateTime if the value is a DateTime.
390    /// Default otherwise.
391    pub fn as_datetime_or_default(&self) -> NaiveDateTime {
392        self.as_datetime_opt().unwrap_or_default()
393    }
394
395    /// Return the content as NaiveDateTime if the value is a DateTime.
396    /// Default otherwise.
397    pub fn as_datetime_or(&self, d: NaiveDateTime) -> NaiveDateTime {
398        self.as_datetime_opt().unwrap_or(d)
399    }
400
401    /// Return the content as an optional NaiveDateTime if the value is
402    /// a DateTime.
403    pub fn as_datetime_opt(&self) -> Option<NaiveDateTime> {
404        match self {
405            Value::DateTime(dt) => Some(*dt),
406            _ => None,
407        }
408    }
409
410    /// Return the content as NaiveDate if the value is a DateTime.
411    /// Default otherwise.
412    pub fn as_date_or_default(&self) -> NaiveDate {
413        self.as_date_opt().unwrap_or_default()
414    }
415
416    /// Return the content as NaiveDate if the value is a DateTime.
417    /// Default otherwise.
418    pub fn as_date_or(&self, d: NaiveDate) -> NaiveDate {
419        self.as_date_opt().unwrap_or(d)
420    }
421
422    /// Return the content as an optional NaiveDateTime if the value is
423    /// a DateTime.
424    pub fn as_date_opt(&self) -> Option<NaiveDate> {
425        match self {
426            Value::DateTime(dt) => Some(dt.date()),
427            _ => None,
428        }
429    }
430
431    /// Returns the currency code or "" if the value is not a currency.
432    pub fn currency(&self) -> &str {
433        match self {
434            Value::Currency(_, c) => c,
435            _ => "",
436        }
437    }
438
439    /// Create a currency value.
440    #[allow(clippy::needless_range_loop)]
441    pub fn new_currency<S: AsRef<str>>(cur: S, value: f64) -> Self {
442        Value::Currency(value, cur.as_ref().into())
443    }
444
445    /// Create a percentage value.
446    pub fn new_percentage(value: f64) -> Self {
447        Value::Percentage(value)
448    }
449}
450
451/// currency value
452#[macro_export]
453macro_rules! currency {
454    ($c:expr, $v:expr) => {
455        Value::new_currency($c, $v as f64)
456    };
457}
458
459/// currency value
460#[macro_export]
461macro_rules! percent {
462    ($v:expr) => {
463        Value::new_percentage($v)
464    };
465}
466
467impl From<()> for Value {
468    fn from(_: ()) -> Self {
469        Value::Empty
470    }
471}
472
473impl From<&str> for Value {
474    fn from(s: &str) -> Self {
475        Value::Text(s.to_string())
476    }
477}
478
479impl From<String> for Value {
480    fn from(s: String) -> Self {
481        Value::Text(s)
482    }
483}
484
485impl From<&String> for Value {
486    fn from(s: &String) -> Self {
487        Value::Text(s.to_string())
488    }
489}
490
491impl From<TextTag> for Value {
492    fn from(t: TextTag) -> Self {
493        Value::TextXml(vec![t])
494    }
495}
496
497impl From<Vec<TextTag>> for Value {
498    fn from(t: Vec<TextTag>) -> Self {
499        Value::TextXml(t)
500    }
501}
502
503impl From<Option<&str>> for Value {
504    fn from(s: Option<&str>) -> Self {
505        if let Some(s) = s {
506            Value::Text(s.to_string())
507        } else {
508            Value::Empty
509        }
510    }
511}
512
513impl From<Option<&String>> for Value {
514    fn from(s: Option<&String>) -> Self {
515        if let Some(s) = s {
516            Value::Text(s.to_string())
517        } else {
518            Value::Empty
519        }
520    }
521}
522
523impl From<Option<String>> for Value {
524    fn from(s: Option<String>) -> Self {
525        if let Some(s) = s {
526            Value::Text(s)
527        } else {
528            Value::Empty
529        }
530    }
531}
532
533#[cfg(feature = "rust_decimal")]
534impl From<Decimal> for Value {
535    fn from(f: Decimal) -> Self {
536        Value::Number(f.to_f64().expect("decimal->f64 should not fail"))
537    }
538}
539
540#[cfg(feature = "rust_decimal")]
541impl From<Option<Decimal>> for Value {
542    fn from(f: Option<Decimal>) -> Self {
543        if let Some(f) = f {
544            Value::Number(f.to_f64().expect("decimal->f64 should not fail"))
545        } else {
546            Value::Empty
547        }
548    }
549}
550
551macro_rules! from_number {
552    ($l:ty) => {
553        impl From<$l> for Value {
554            #![allow(trivial_numeric_casts)]
555            fn from(f: $l) -> Self {
556                Value::Number(f as f64)
557            }
558        }
559
560        impl From<&$l> for Value {
561            #![allow(trivial_numeric_casts)]
562            fn from(f: &$l) -> Self {
563                Value::Number(*f as f64)
564            }
565        }
566
567        impl From<Option<$l>> for Value {
568            #![allow(trivial_numeric_casts)]
569            fn from(f: Option<$l>) -> Self {
570                if let Some(f) = f {
571                    Value::Number(f as f64)
572                } else {
573                    Value::Empty
574                }
575            }
576        }
577
578        impl From<Option<&$l>> for Value {
579            #![allow(trivial_numeric_casts)]
580            fn from(f: Option<&$l>) -> Self {
581                if let Some(f) = f {
582                    Value::Number(*f as f64)
583                } else {
584                    Value::Empty
585                }
586            }
587        }
588    };
589}
590
591from_number!(f64);
592from_number!(f32);
593from_number!(i64);
594from_number!(i32);
595from_number!(i16);
596from_number!(i8);
597from_number!(u64);
598from_number!(u32);
599from_number!(u16);
600from_number!(u8);
601
602impl From<bool> for Value {
603    fn from(b: bool) -> Self {
604        Value::Boolean(b)
605    }
606}
607
608impl From<Option<bool>> for Value {
609    fn from(b: Option<bool>) -> Self {
610        if let Some(b) = b {
611            Value::Boolean(b)
612        } else {
613            Value::Empty
614        }
615    }
616}
617
618impl From<NaiveDateTime> for Value {
619    fn from(dt: NaiveDateTime) -> Self {
620        Value::DateTime(dt)
621    }
622}
623
624impl From<Option<NaiveDateTime>> for Value {
625    fn from(dt: Option<NaiveDateTime>) -> Self {
626        if let Some(dt) = dt {
627            Value::DateTime(dt)
628        } else {
629            Value::Empty
630        }
631    }
632}
633
634impl From<NaiveDate> for Value {
635    fn from(dt: NaiveDate) -> Self {
636        Value::DateTime(dt.and_hms_opt(0, 0, 0).unwrap())
637    }
638}
639
640impl From<Option<NaiveDate>> for Value {
641    fn from(dt: Option<NaiveDate>) -> Self {
642        if let Some(dt) = dt {
643            Value::DateTime(dt.and_hms_opt(0, 0, 0).expect("valid time"))
644        } else {
645            Value::Empty
646        }
647    }
648}
649
650impl From<NaiveTime> for Value {
651    fn from(ti: NaiveTime) -> Self {
652        Value::DateTime(NaiveDateTime::new(
653            NaiveDate::from_ymd_opt(1899, 12, 30).expect("valid date"),
654            ti,
655        ))
656    }
657}
658
659impl From<Option<NaiveTime>> for Value {
660    fn from(dt: Option<NaiveTime>) -> Self {
661        if let Some(ti) = dt {
662            Value::DateTime(NaiveDateTime::new(
663                NaiveDate::from_ymd_opt(1899, 12, 30).expect("valid date"),
664                ti,
665            ))
666        } else {
667            Value::Empty
668        }
669    }
670}
671
672impl From<Duration> for Value {
673    fn from(d: Duration) -> Self {
674        Value::TimeDuration(d)
675    }
676}
677
678impl From<Option<Duration>> for Value {
679    fn from(d: Option<Duration>) -> Self {
680        if let Some(d) = d {
681            Value::TimeDuration(d)
682        } else {
683            Value::Empty
684        }
685    }
686}