1use std::cmp::Ordering;
3
4use serde::{Deserialize, Serialize};
5
6#[derive(Serialize, Deserialize, Clone, Eq, PartialEq, Hash, Debug, Default)]
8#[cfg_attr(test, serde(deny_unknown_fields))]
9#[allow(missing_docs)]
10#[non_exhaustive]
11pub struct Price {
12 pub usd: Option<String>,
13 pub usd_foil: Option<String>,
14 pub eur: Option<String>,
15 pub eur_foil: Option<String>,
16 pub tix: Option<String>,
17 pub usd_etched: Option<String>,
18}
19
20impl Price {
21 fn to_array(&self) -> [&Option<String>; 5] {
23 [
24 &self.usd,
25 &self.usd_foil,
26 &self.eur,
27 &self.eur_foil,
28 &self.tix,
29 ]
30 }
31}
32
33fn compare_prices(a: &Option<String>, b: &Option<String>) -> Option<Ordering> {
35 if let (Some(a), Some(b)) = (a, b) {
36 if let (Ok(a), Ok(b)) = (a.parse::<f32>(), b.parse()) {
37 return a.partial_cmp(&b);
38 }
39 }
40 None
41}
42
43impl PartialOrd for Price {
44 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
45 let mut result = None;
46 for (a, b) in self.to_array().iter().zip(other.to_array().iter()) {
47 match (result, compare_prices(a, b)) {
48 (None, order)
51 | (order, None)
52 | (Some(Ordering::Equal), order)
53 | (order, Some(Ordering::Equal)) => {
54 result = order;
55 },
56 (Some(a), Some(b)) if a == b => {},
58 _ => return None,
60 }
61 }
62 result
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn no_prices() {
72 let a = Price::default();
73 let b = Price::default();
74
75 assert_eq!(a.partial_cmp(&b), None);
76 }
77
78 #[test]
79 fn prices_agree() {
80 let a = Price {
81 usd: Some("5".to_string()),
82 usd_foil: Some("8".to_string()),
83 eur: Some("3".to_string()),
84 ..Default::default()
85 };
86 let b = Price {
87 usd: Some("10".to_string()),
88 usd_foil: Some("14".to_string()),
89 tix: Some("1".to_string()),
90 ..Default::default()
91 };
92
93 assert_eq!(a.partial_cmp(&b), Some(Ordering::Less));
94 }
95
96 #[test]
97 fn prices_disagree() {
98 let a = Price {
99 usd: Some("0.1".to_string()),
100 tix: Some("15".to_string()),
101 ..Default::default()
102 };
103 let b = Price {
104 usd: Some("2".to_string()),
105 tix: Some(".5".to_string()),
106 ..Default::default()
107 };
108
109 assert_eq!(a.partial_cmp(&b), None);
110 }
111
112 #[test]
113 fn prices_equal() {
114 let a = Price {
115 usd: Some("3.99".to_string()),
116 tix: Some("2.1".to_string()),
117 ..Default::default()
118 };
119 let b = Price {
120 usd: Some("3.99".to_string()),
121 eur: Some("4.20".to_string()),
122 ..Default::default()
123 };
124
125 assert_eq!(a.partial_cmp(&b), Some(Ordering::Equal));
126 }
127}