use std::collections::BTreeMap;
use std::fmt;
use serde::de::{self, MapAccess, SeqAccess, Visitor};
use serde::{Deserializer, Serialize, Serializer};
pub fn serialize<S>(map: &BTreeMap<i64, i64>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
map.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<BTreeMap<i64, i64>, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_any(MappingVisitor)
}
struct MappingVisitor;
impl<'de> Visitor<'de> for MappingVisitor {
type Value = BTreeMap<i64, i64>;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a map of integer→integer entries or an array of [old, new] integer pairs")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let mut map = BTreeMap::new();
while let Some((old, new)) = seq.next_element::<(i64, i64)>()? {
if map.insert(old, new).is_some() {
return Err(de::Error::custom(format!(
"duplicate enum remap key: {old} appears more than once"
)));
}
}
Ok(map)
}
fn visit_map<A>(self, mut access: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut map = BTreeMap::new();
while let Some((key, value)) = access.next_entry::<i64, i64>()? {
if map.insert(key, value).is_some() {
return Err(de::Error::custom(format!(
"duplicate enum remap key: {key} appears more than once"
)));
}
}
Ok(map)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Wrap {
#[serde(with = "super")]
mapping: BTreeMap<i64, i64>,
}
fn fixture() -> Wrap {
let mut m = BTreeMap::new();
m.insert(5, 100);
m.insert(100, 101);
Wrap { mapping: m }
}
#[test]
fn serializes_to_map_form_in_json() {
let json = serde_json::to_string(&fixture()).unwrap();
assert_eq!(json, r#"{"mapping":{"5":100,"100":101}}"#);
}
#[test]
fn deserializes_map_form_from_json() {
let json = r#"{"mapping":{"5":100,"100":101}}"#;
let parsed: Wrap = serde_json::from_str(json).unwrap();
assert_eq!(parsed, fixture());
}
#[test]
fn deserializes_legacy_array_form_from_json() {
let json = r#"{"mapping":[[5,100],[100,101]]}"#;
let parsed: Wrap = serde_json::from_str(json).unwrap();
assert_eq!(parsed, fixture());
}
#[test]
fn map_form_round_trip_is_stable_in_json() {
let json = serde_json::to_string(&fixture()).unwrap();
let parsed: Wrap = serde_json::from_str(&json).unwrap();
let json2 = serde_json::to_string(&parsed).unwrap();
assert_eq!(json, json2);
}
#[test]
fn rejects_duplicate_keys_in_legacy_array_form() {
let json = r#"{"mapping":[[5,100],[5,200]]}"#;
let err = serde_json::from_str::<Wrap>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("duplicate enum remap key"),
"expected duplicate-key error, got: {msg}"
);
}
#[test]
fn empty_mapping_round_trips() {
let empty = Wrap {
mapping: BTreeMap::new(),
};
let json = serde_json::to_string(&empty).unwrap();
assert_eq!(json, r#"{"mapping":{}}"#);
let parsed: Wrap = serde_json::from_str(&json).unwrap();
assert_eq!(parsed, empty);
}
#[test]
fn rejects_non_map_non_seq_value_invokes_expecting() {
let json = r#"{"mapping": true}"#;
let err = serde_json::from_str::<Wrap>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("integer\u{2192}integer") || msg.contains("array of"),
"expected expecting() text in error, got: {msg}"
);
}
#[test]
fn rejects_non_map_non_seq_string_invokes_expecting() {
let json = r#"{"mapping": "not-a-map"}"#;
let err = serde_json::from_str::<Wrap>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("integer\u{2192}integer") || msg.contains("array of"),
"expected expecting() text in error, got: {msg}"
);
}
#[test]
fn rejects_duplicate_keys_in_canonical_map_form() {
let json = r#"{"mapping":{"5":100,"5":200}}"#;
let err = serde_json::from_str::<Wrap>(json).unwrap_err();
let msg = err.to_string();
assert!(
msg.contains("duplicate enum remap key"),
"expected duplicate-key error from visit_map, got: {msg}"
);
}
#[test]
fn visit_map_iterates_multi_entry_canonical_form() {
let json = r#"{"mapping":{"1":10,"2":20,"3":30,"4":40}}"#;
let parsed: Wrap = serde_json::from_str(json).unwrap();
assert_eq!(parsed.mapping.len(), 4);
assert_eq!(parsed.mapping.get(&3), Some(&30));
}
}