Skip to main content

spreadsheet_ods/
value_.rs

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