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#[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#[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 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 pub fn as_bool_or(&self, d: bool) -> bool {
75 match self {
76 Value::Boolean(b) => *b,
77 _ => d,
78 }
79 }
80
81 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 #[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 #[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 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 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 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 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 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 pub fn as_timeduration_or(&self, d: Duration) -> Duration {
342 match self {
343 Value::TimeDuration(td) => *td,
344 _ => d,
345 }
346 }
347
348 pub fn as_timeduration_opt(&self) -> Option<Duration> {
351 match self {
352 Value::TimeDuration(td) => Some(*td),
353 _ => None,
354 }
355 }
356
357 pub fn as_datetime_or(&self, d: NaiveDateTime) -> NaiveDateTime {
360 match self {
361 Value::DateTime(dt) => *dt,
362 _ => d,
363 }
364 }
365
366 pub fn as_datetime_opt(&self) -> Option<NaiveDateTime> {
369 match self {
370 Value::DateTime(dt) => Some(*dt),
371 _ => None,
372 }
373 }
374
375 pub fn as_date_or(&self, d: NaiveDate) -> NaiveDate {
378 match self {
379 Value::DateTime(dt) => dt.date(),
380 _ => d,
381 }
382 }
383
384 pub fn as_date_opt(&self) -> Option<NaiveDate> {
387 match self {
388 Value::DateTime(dt) => Some(dt.date()),
389 _ => None,
390 }
391 }
392
393 pub fn currency(&self) -> &str {
395 match self {
396 Value::Currency(_, c) => c,
397 _ => "",
398 }
399 }
400
401 #[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 pub fn new_percentage(value: f64) -> Self {
409 Value::Percentage(value)
410 }
411}
412
413#[macro_export]
415macro_rules! currency {
416 ($c:expr, $v:expr) => {
417 Value::new_currency($c, $v as f64)
418 };
419}
420
421#[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}