use std::cell::RefCell;
use std::collections::HashMap;
use anyhow::Result;
use serde::de::Error;
use serde::{Deserialize, Deserializer};
use serde_context::{context_scope, deserialize_with_context};
const JSON_FRUITS: &str = r#"["apple", "banana", "orange", "banana"]"#;
type IdAlloc = RefCell<HashMap<String, Id>>;
#[derive(Copy, Clone, PartialEq, Debug)]
struct Id(usize);
impl<'de> Deserialize<'de> for Id {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
context_scope(|cx| {
let mut alloc = cx.get::<IdAlloc>().map_err(D::Error::custom)?.borrow_mut();
let name = String::deserialize(deserializer)?;
let next_index = alloc.len();
let id = *alloc.entry(name).or_insert(Id(next_index));
Ok(id)
})
}
}
fn main() -> Result<()> {
let alloc = IdAlloc::default();
let mut deserializer = serde_json::Deserializer::from_str(JSON_FRUITS);
let ids: Vec<Id> = deserialize_with_context(&mut deserializer, &alloc)?;
assert_eq!(ids, vec![Id(0), Id(1), Id(2), Id(1)]);
let keys = alloc.into_inner();
assert_eq!(keys.get("apple"), Some(&Id(0)));
assert_eq!(keys.get("banana"), Some(&Id(1)));
assert_eq!(keys.get("orange"), Some(&Id(2)));
assert_eq!(keys.get("peach"), None);
Ok(())
}