1use std::{collections::BTreeMap, convert::TryInto, fmt, marker::PhantomData};
2
3use js_int::{Int, UInt};
4use serde::{
5 de::{self, Deserializer, IntoDeserializer as _, MapAccess, Visitor},
6 ser::Serializer,
7 Deserialize, Serialize,
8};
9
10pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
21where
22 D: Deserializer<'de>,
23 T: Deserialize<'de>,
24{
25 let opt = Option::<String>::deserialize(de)?;
26 match opt.as_deref() {
27 None | Some("") => Ok(None),
28 Some(s) => T::deserialize(s.into_deserializer()).map(Some),
31 }
32}
33
34pub fn none_as_empty_string<T: Serialize, S>(
40 value: &Option<T>,
41 serializer: S,
42) -> Result<S::Ok, S::Error>
43where
44 S: Serializer,
45{
46 match value {
47 Some(x) => x.serialize(serializer),
48 None => serializer.serialize_str(""),
49 }
50}
51
52pub fn deserialize_v1_powerlevel<'de, D>(de: D) -> Result<Int, D::Error>
57where
58 D: Deserializer<'de>,
59{
60 struct IntOrStringVisitor;
61
62 impl<'de> Visitor<'de> for IntOrStringVisitor {
63 type Value = Int;
64
65 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
66 formatter.write_str("an integer or a string")
67 }
68
69 fn visit_i8<E: de::Error>(self, v: i8) -> Result<Self::Value, E> {
70 Ok(v.into())
71 }
72
73 fn visit_i16<E: de::Error>(self, v: i16) -> Result<Self::Value, E> {
74 Ok(v.into())
75 }
76
77 fn visit_i32<E: de::Error>(self, v: i32) -> Result<Self::Value, E> {
78 Ok(v.into())
79 }
80
81 fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
82 v.try_into().map_err(E::custom)
83 }
84
85 fn visit_i128<E: de::Error>(self, v: i128) -> Result<Self::Value, E> {
86 v.try_into().map_err(E::custom)
87 }
88
89 fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> {
90 Ok(v.into())
91 }
92
93 fn visit_u16<E: de::Error>(self, v: u16) -> Result<Self::Value, E> {
94 Ok(v.into())
95 }
96
97 fn visit_u32<E: de::Error>(self, v: u32) -> Result<Self::Value, E> {
98 Ok(v.into())
99 }
100
101 fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
102 v.try_into().map_err(E::custom)
103 }
104
105 fn visit_u128<E: de::Error>(self, v: u128) -> Result<Self::Value, E> {
106 v.try_into().map_err(E::custom)
107 }
108
109 fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
110 let trimmed = v.trim();
111
112 match trimmed.strip_prefix('+') {
113 Some(without) => without.parse::<UInt>().map(|u| u.into()).map_err(E::custom),
114 None => trimmed.parse().map_err(E::custom),
115 }
116 }
117 }
118
119 de.deserialize_any(IntOrStringVisitor)
120}
121
122pub fn btreemap_deserialize_v1_powerlevel_values<'de, D, T>(
128 de: D,
129) -> Result<BTreeMap<T, Int>, D::Error>
130where
131 D: Deserializer<'de>,
132 T: Deserialize<'de> + Ord,
133{
134 #[repr(transparent)]
135 struct IntWrap(Int);
136
137 impl<'de> Deserialize<'de> for IntWrap {
138 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
139 where
140 D: Deserializer<'de>,
141 {
142 deserialize_v1_powerlevel(deserializer).map(IntWrap)
143 }
144 }
145
146 struct IntMapVisitor<T> {
147 _phantom: PhantomData<T>,
148 }
149
150 impl<T> IntMapVisitor<T> {
151 fn new() -> Self {
152 Self { _phantom: PhantomData }
153 }
154 }
155
156 impl<'de, T> Visitor<'de> for IntMapVisitor<T>
157 where
158 T: Deserialize<'de> + Ord,
159 {
160 type Value = BTreeMap<T, Int>;
161
162 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
163 formatter.write_str("a map with integers or stings as values")
164 }
165
166 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
167 let mut res = BTreeMap::new();
168
169 while let Some((k, IntWrap(v))) = map.next_entry()? {
170 res.insert(k, v);
171 }
172
173 Ok(res)
174 }
175 }
176
177 de.deserialize_map(IntMapVisitor::new())
178}
179
180#[cfg(test)]
181mod tests {
182 use js_int::{int, Int};
183 use matches::assert_matches;
184 use serde::Deserialize;
185
186 use super::deserialize_v1_powerlevel;
187
188 #[derive(Debug, Deserialize)]
189 struct Test {
190 #[serde(deserialize_with = "deserialize_v1_powerlevel")]
191 num: Int,
192 }
193
194 #[test]
195 fn int_or_string() -> serde_json::Result<()> {
196 assert_matches!(
197 serde_json::from_value::<Test>(serde_json::json!({ "num": "0" }))?,
198 Test { num } if num == int!(0)
199 );
200
201 Ok(())
202 }
203
204 #[test]
205 fn weird_plus_string() -> serde_json::Result<()> {
206 assert_matches!(
207 serde_json::from_value::<Test>(serde_json::json!({ "num": " +0000000001000 " }))?,
208 Test { num } if num == int!(1000)
209 );
210
211 Ok(())
212 }
213
214 #[test]
215 fn weird_minus_string() -> serde_json::Result<()> {
216 assert_matches!(
217 serde_json::from_value::<Test>(serde_json::json!({ "num": " \n\n-0000000000000001000 " }))?,
218 Test { num } if num == int!(-1000)
219 );
220
221 Ok(())
222 }
223}