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#[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#[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 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 pub fn as_bool_or(&self, d: bool) -> bool {
78 match self {
79 Value::Boolean(b) => *b,
80 _ => d,
81 }
82 }
83
84 pub fn as_i64_or_default(&self) -> i64 {
87 self.as_i64_opt().unwrap_or_default()
88 }
89
90 pub fn as_i64_or(&self, d: i64) -> i64 {
93 self.as_i64_opt().unwrap_or(d)
94 }
95
96 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 pub fn as_u64_or_default(&self) -> u64 {
110 self.as_u64_opt().unwrap_or_default()
111 }
112
113 pub fn as_u64_or(&self, d: u64) -> u64 {
116 self.as_u64_opt().unwrap_or(d)
117 }
118
119 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 pub fn as_i32_or_default(&self) -> i32 {
133 self.as_i32_opt().unwrap_or_default()
134 }
135
136 pub fn as_i32_or(&self, d: i32) -> i32 {
139 self.as_i32_opt().unwrap_or(d)
140 }
141
142 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 pub fn as_u32_or_default(&self) -> u32 {
156 self.as_u32_opt().unwrap_or_default()
157 }
158
159 pub fn as_u32_or(&self, d: u32) -> u32 {
162 self.as_u32_opt().unwrap_or(d)
163 }
164
165 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 pub fn as_i16_or_default(&self) -> i16 {
179 self.as_i16_opt().unwrap_or_default()
180 }
181
182 pub fn as_i16_or(&self, d: i16) -> i16 {
185 self.as_i16_opt().unwrap_or(d)
186 }
187
188 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 pub fn as_u16_or_default(&self) -> u16 {
202 self.as_u16_opt().unwrap_or_default()
203 }
204
205 pub fn as_u16_or(&self, d: u16) -> u16 {
208 self.as_u16_opt().unwrap_or(d)
209 }
210
211 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 pub fn as_i8_or_default(&self) -> i8 {
225 self.as_i8_opt().unwrap_or_default()
226 }
227
228 pub fn as_i8_or(&self, d: i8) -> i8 {
231 self.as_i8_opt().unwrap_or(d)
232 }
233
234 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 pub fn as_u8_or_default(&self) -> u8 {
248 self.as_u8_opt().unwrap_or_default()
249 }
250
251 pub fn as_u8_or(&self, d: u8) -> u8 {
254 self.as_u8_opt().unwrap_or(d)
255 }
256
257 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 #[cfg(feature = "rust_decimal")]
271 pub fn as_decimal_or_default(&self) -> Decimal {
272 self.as_decimal_opt().unwrap_or_default()
273 }
274
275 #[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 #[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 pub fn as_f64_or_default(&self) -> f64 {
297 self.as_f64_opt().unwrap_or_default()
298 }
299
300 pub fn as_f64_or(&self, d: f64) -> f64 {
303 self.as_f64_opt().unwrap_or(d)
304 }
305
306 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 pub fn as_str_or_default(&self) -> &str {
319 self.as_str_opt().unwrap_or_default()
320 }
321
322 pub fn as_str_or<'a>(&'a self, d: &'a str) -> &'a str {
324 self.as_str_opt().unwrap_or(d)
325 }
326
327 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 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 pub fn as_string_or_default(&self) -> String {
359 self.as_string_opt().unwrap_or_default()
360 }
361
362 pub fn as_string_opt(&self) -> Option<String> {
364 match self {
365 Value::Text(s) => Some(s.clone()),
366 _ => None,
367 }
368 }
369
370 pub fn as_timeduration_or_default(&self) -> Duration {
373 self.as_timeduration_opt().unwrap_or_default()
374 }
375
376 pub fn as_timeduration_or(&self, d: Duration) -> Duration {
379 self.as_timeduration_opt().unwrap_or(d)
380 }
381
382 pub fn as_timeduration_opt(&self) -> Option<Duration> {
385 match self {
386 Value::TimeDuration(td) => Some(*td),
387 _ => None,
388 }
389 }
390
391 pub fn as_datetime_or_default(&self) -> NaiveDateTime {
394 self.as_datetime_opt().unwrap_or_default()
395 }
396
397 pub fn as_datetime_or(&self, d: NaiveDateTime) -> NaiveDateTime {
400 self.as_datetime_opt().unwrap_or(d)
401 }
402
403 pub fn as_datetime_opt(&self) -> Option<NaiveDateTime> {
406 match self {
407 Value::DateTime(dt) => Some(*dt),
408 _ => None,
409 }
410 }
411
412 pub fn as_date_or_default(&self) -> NaiveDate {
415 self.as_date_opt().unwrap_or_default()
416 }
417
418 pub fn as_date_or(&self, d: NaiveDate) -> NaiveDate {
421 self.as_date_opt().unwrap_or(d)
422 }
423
424 pub fn as_date_opt(&self) -> Option<NaiveDate> {
427 match self {
428 Value::DateTime(dt) => Some(dt.date()),
429 _ => None,
430 }
431 }
432
433 pub fn currency(&self) -> &str {
435 match self {
436 Value::Currency(_, c) => c,
437 _ => "",
438 }
439 }
440
441 #[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 pub fn new_percentage(value: f64) -> Self {
449 Value::Percentage(value)
450 }
451}
452
453#[macro_export]
455macro_rules! currency {
456 ($c:expr, $v:expr) => {
457 Value::new_currency($c, $v as f64)
458 };
459}
460
461#[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}