use std::collections::HashMap; use std::fmt::Formatter; use std::marker::PhantomData; use serde::{Deserialize, Deserializer}; use serde::de::{MapAccess, Visitor}; use sha1_smol::Digest; #[derive(Debug, Deserialize)] pub struct Asset { #[serde(skip)] pub name: String, pub hash: Digest, pub size: usize } #[derive(Debug, Deserialize)] pub struct AssetIndex { #[serde(rename = "virtual", default)] pub virtual_assets: bool, #[serde(default)] pub map_to_resources: bool, #[serde(deserialize_with = "deserialize_assets")] pub objects: HashMap } trait SetName { fn set_name(&mut self, name: String); } impl SetName for Asset { fn set_name(&mut self, name: String) { self.name = name; } } fn deserialize_assets<'de, D, T>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, T: SetName + Deserialize<'de> { struct AssetVisitor(PhantomData); impl<'de, T> Visitor<'de> for AssetVisitor where T: SetName + Deserialize<'de> { type Value = HashMap; fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { formatter.write_str("asset objects map") } fn visit_map(self, mut map: A) -> Result where A: MapAccess<'de>, { let mut out = HashMap::new(); while let Some((key, mut asset)) = map.next_entry::()? { asset.set_name(key.clone()); out.insert(key, asset); } Ok(out) } } deserializer.deserialize_any(AssetVisitor(PhantomData)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_it() { dbg!(serde_json::from_str::(r#"{ "virtual": true, "objects": { "object1": { "hash": "0d000710b71ca9aafabd8f587768431d0b560b32", "size": 100 }, "object2/abc": { "hash": "0e000710b71ca9aafabd8f587768431d0b560b32", "size": 10000 } } }"#).unwrap()); } }