use serde::de::{self, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer};
use std::collections::HashMap;
use std::fmt;
use std::fmt::Display;
use std::hash::Hash;
use std::marker::PhantomData;
use std::str::FromStr;
pub(crate) mod private {
pub trait Sealed {}
}
pub(crate) fn number_from_string<'de, T, D>(deserializer: D) -> std::result::Result<T, D::Error>
where
D: Deserializer<'de>,
T: FromStr + Deserialize<'de>,
<T as FromStr>::Err: Display,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum StringOrNumber<T> {
String(String),
Number(T),
}
let value = StringOrNumber::deserialize(deserializer)?;
match value {
StringOrNumber::Number(n) => Ok(n),
StringOrNumber::String(s) => s.parse::<T>().map_err(de::Error::custom),
}
}
pub(crate) fn empty_string_is_none<'de, D>(deserializer: D) -> Result<Option<String>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s.is_empty() { Ok(None) } else { Ok(Some(s)) }
}
struct MapOrSeq<K, V>(PhantomData<(K, V)>);
impl<'de, K, V> Visitor<'de> for MapOrSeq<K, V>
where
K: Eq + Hash + Deserialize<'de>,
V: Deserialize<'de>,
{
type Value = HashMap<K, V>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a map or an array")
}
fn visit_map<A>(self, mut access: A) -> std::result::Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut map = HashMap::with_capacity(access.size_hint().unwrap_or(0));
while let Some((k, v)) = access.next_entry()? {
map.insert(k, v);
}
Ok(map)
}
fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut map = HashMap::new();
let mut index: usize = 0;
while let Some(value) = seq.next_element::<V>()? {
let key = K::deserialize(serde_json::Value::from(index)).map_err(de::Error::custom)?;
map.insert(key, value);
index += 1;
}
Ok(map)
}
}
pub(crate) fn map_or_seq<'de, D, K, V>(
deserializer: D,
) -> std::result::Result<HashMap<K, V>, D::Error>
where
D: Deserializer<'de>,
K: Eq + Hash + Deserialize<'de>,
V: Deserialize<'de>,
{
deserializer.deserialize_any(MapOrSeq(PhantomData))
}
#[derive(Debug)]
pub(crate) struct WireMap<K, V>(pub HashMap<K, V>);
impl<'de, K, V> Deserialize<'de> for WireMap<K, V>
where
K: Eq + Hash + Deserialize<'de>,
V: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
map_or_seq(deserializer).map(WireMap)
}
}
pub(crate) fn validate_name(name: &str, extra: &[char]) -> bool {
name.chars()
.all(|c| c.is_ascii_alphanumeric() || extra.contains(&c))
}
#[cfg(test)]
mod tests {
use super::*;
use serde::Deserialize;
#[derive(Debug, Deserialize)]
struct Item {
name: String,
}
#[derive(Debug, Deserialize)]
struct Id {
#[serde(deserialize_with = "number_from_string")]
id: u32,
}
#[derive(Debug, Deserialize)]
struct Uuid {
#[serde(deserialize_with = "empty_string_is_none")]
uuid: Option<String>,
}
#[derive(Debug, Deserialize)]
struct Links {
#[serde(deserialize_with = "map_or_seq")]
ethernet: HashMap<i32, String>,
#[serde(deserialize_with = "map_or_seq")]
serial: HashMap<i32, HashMap<i32, String>>,
}
#[test]
fn string_number_becomes_number() {
let id: Id = serde_json::from_str(r#"{"id":"1"}"#).unwrap();
assert_eq!(id.id, 1);
}
#[test]
fn number_remains_number() {
let id: Id = serde_json::from_str(r#"{"id":1}"#).unwrap();
assert_eq!(id.id, 1);
}
#[test]
fn empty_string_becomes_none() {
let uuid: Uuid = serde_json::from_str(r#"{"uuid":""}"#).unwrap();
assert_eq!(uuid.uuid, None);
}
#[test]
fn string_remains_string() {
let uuid: Uuid =
serde_json::from_str(r#"{"uuid":"6d88a1b5-64db-46c2-9aa5-ca4fca0811c2"}"#).unwrap();
assert_eq!(
uuid.uuid,
Some("6d88a1b5-64db-46c2-9aa5-ca4fca0811c2".to_string())
);
}
#[test]
fn map_stays_a_map() {
let m: Links = serde_json::from_str(
r#"{"ethernet":{"1":"a","16":"b","49":"c"},"serial":{"1":{"3":"a"}}}"#,
)
.unwrap();
assert_eq!(m.ethernet[&1], "a");
assert_eq!(m.serial[&1][&3], "a");
}
#[test]
fn array_becomes_a_map() {
let h: Links = serde_json::from_str(r#"{"ethernet":["a", "b", "c"],"serial":[]}"#).unwrap();
assert_eq!(h.ethernet[&0], "a");
assert!(h.serial.is_empty());
}
#[test]
fn string_keys_convert_to_integers() {
let wire: WireMap<i32, Item> = serde_json::from_str(r#"{"1":{"name":"a"}}"#).unwrap();
assert_eq!(wire.0[&1].name, "a");
}
#[test]
fn wire_map_works_inside_response() {
let r: crate::client::Response<WireMap<i32, Item>> =
serde_json::from_str(r#"{"code":200,"status":"success","message":"ok","data":[]}"#)
.unwrap();
assert!(r.data.unwrap().0.is_empty());
}
}