spreadsheet_ods/
value_.rs

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