salvo_oapi/openapi/schema/
number.rs1use serde::de::{self, Visitor};
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4#[derive(Clone, Debug)]
20pub enum Number {
21 Int(isize),
23 UInt(usize),
25 Float(f64),
27}
28
29impl Eq for Number {}
30
31impl PartialEq for Number {
32 fn eq(&self, other: &Self) -> bool {
33 match (self, other) {
34 (Self::Int(left), Self::Int(right)) => left == right,
35 (Self::UInt(left), Self::UInt(right)) => left == right,
36 (Self::Float(left), Self::Float(right)) => left == right,
37 _ => false,
38 }
39 }
40}
41
42impl Serialize for Number {
43 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
44 where
45 S: Serializer,
46 {
47 match self {
48 Self::Int(value) => serializer.serialize_i64(*value as i64),
49 Self::UInt(value) => serializer.serialize_u64(*value as u64),
50 Self::Float(value) => {
51 if value.fract() == 0.0 && value.is_finite() {
53 if *value < 0.0 {
54 serializer.serialize_i64(*value as i64)
55 } else {
56 serializer.serialize_u64(*value as u64)
57 }
58 } else {
59 serializer.serialize_f64(*value)
60 }
61 }
62 }
63 }
64}
65
66struct NumberVisitor;
67
68impl<'de> Visitor<'de> for NumberVisitor {
69 type Value = Number;
70
71 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
72 formatter.write_str("a number (integer or float)")
73 }
74
75 fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
76 where
77 E: de::Error,
78 {
79 Ok(Number::Int(v as isize))
80 }
81
82 fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
83 where
84 E: de::Error,
85 {
86 Ok(Number::UInt(v as usize))
87 }
88
89 fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
90 where
91 E: de::Error,
92 {
93 Ok(Number::Float(v))
94 }
95}
96
97impl<'de> Deserialize<'de> for Number {
98 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
99 where
100 D: Deserializer<'de>,
101 {
102 deserializer.deserialize_any(NumberVisitor)
103 }
104}
105
106macro_rules! impl_from_for_number {
107 ( $( $ty:ident => $pat:ident $( as $as:ident )? ),* ) => {
108 $(
109 impl From<$ty> for Number {
110 fn from(value: $ty) -> Self {
111 Self::$pat(value $( as $as )?)
112 }
113 }
114 )*
115 };
116}
117
118#[rustfmt::skip]
119impl_from_for_number!(
120 f32 => Float as f64, f64 => Float,
121 i8 => Int as isize, i16 => Int as isize, i32 => Int as isize, i64 => Int as isize,
122 u8 => UInt as usize, u16 => UInt as usize, u32 => UInt as usize, u64 => UInt as usize,
123 isize => Int, usize => UInt
124);
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn test_serialize_int() {
132 let n = Number::Int(42);
133 assert_eq!(serde_json::to_string(&n).unwrap(), "42");
134 }
135
136 #[test]
137 fn test_serialize_negative_int() {
138 let n = Number::Int(-5);
139 assert_eq!(serde_json::to_string(&n).unwrap(), "-5");
140 }
141
142 #[test]
143 fn test_serialize_uint() {
144 let n = Number::UInt(100);
145 assert_eq!(serde_json::to_string(&n).unwrap(), "100");
146 }
147
148 #[test]
149 fn test_serialize_float() {
150 let n = Number::Float(3.14);
151 assert_eq!(serde_json::to_string(&n).unwrap(), "3.14");
152 }
153
154 #[test]
155 fn test_serialize_whole_float_as_integer() {
156 let n = Number::Float(10.0);
157 assert_eq!(serde_json::to_string(&n).unwrap(), "10");
158 }
159
160 #[test]
161 fn test_serialize_negative_whole_float() {
162 let n = Number::Float(-3.0);
163 assert_eq!(serde_json::to_string(&n).unwrap(), "-3");
164 }
165
166 #[test]
167 fn test_from_i32() {
168 let n: Number = 42i32.into();
169 assert_eq!(n, Number::Int(42));
170 }
171
172 #[test]
173 fn test_from_u64() {
174 let n: Number = 100u64.into();
175 assert_eq!(n, Number::UInt(100));
176 }
177
178 #[test]
179 fn test_from_f64() {
180 let n: Number = 2.5f64.into();
181 assert_eq!(n, Number::Float(2.5));
182 }
183
184 #[test]
185 fn test_deserialize_int() {
186 let n: Number = serde_json::from_str("42").unwrap();
187 assert_eq!(n, Number::UInt(42));
188 }
189
190 #[test]
191 fn test_deserialize_negative_int() {
192 let n: Number = serde_json::from_str("-5").unwrap();
193 assert_eq!(n, Number::Int(-5));
194 }
195
196 #[test]
197 fn test_deserialize_float() {
198 let n: Number = serde_json::from_str("3.14").unwrap();
199 assert_eq!(n, Number::Float(3.14));
200 }
201
202 #[test]
203 fn test_equality() {
204 assert_eq!(Number::Int(1), Number::Int(1));
205 assert_eq!(Number::UInt(1), Number::UInt(1));
206 assert_eq!(Number::Float(1.5), Number::Float(1.5));
207 assert_ne!(Number::Int(1), Number::UInt(1));
208 }
209}