I am trying to use generics to deserialize structs from file for use with a Swagger generated API. So I have hacked together this which almost works, but I am unable to unpack the external Struct object from the "Owned" pointer, as you can see in the tests.
This might be the wrong strategy, but the problem is that I have various yaml files, which I want to read in and deserialise hinting the correct Struct to deserialise as. I don't want to implement a "readfile" function for each Struct, as there are many. So I am trying to make this generic lib work which should deserialise into the correct Struct, and use with the Swagger API.
It's very close to working but I cannot seem to unwrap the Outer<ExternalStructA> into just ExternalStructA.
Owned(ExternalStructA { x: 1, y: 2 }) Owned(ExternalStructB { a: 1, b: 2 }) lib.rs:
#[cfg(test)] mod tests { use crate::generics_yaml_deserializer::Outer; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; #[derive(Debug, Serialize, Deserialize)] pub struct ExternalStructA { x: u32, y: u32, } #[derive(Debug, Serialize, Deserialize)] pub struct ExternalStructB { a: u64, b: u64, } #[test] fn deserialize() { let a = r#"--- ptr: x: 1 y: 2 "#; let b = r#"--- ptr: a: 1 b: 2 "#; let resulta: Outer<ExternalStructA> = serde_yaml::from_str(a).unwrap(); assert_eq!(1, resulta.ptr.x); // I can't seem to get into ptr ExternalStructA let resultb: Outer<ExternalStructB> = serde_yaml::from_str(b).unwrap(); assert_eq!(1, resultb.ptr.a); // I can't seem to get into ptr ExternalStructB } } mod generics_yaml_deserializer { use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use std::error::Error; // empty holding struct which owns a owned ptr #[derive(Deserialize, Debug)] pub struct Outer<'a, T: 'a + ?Sized> { #[serde(bound(deserialize = "Ptr<'a, T>: Deserialize<'de>"))] pub ptr: Ptr<'a, T>, } #[derive(Debug)] pub enum Ptr<'a, T: 'a + ?Sized> { Ref(&'a T), Owned(Box<T>), } impl<'de, 'a, T: 'a + ?Sized> Deserialize<'de> for Ptr<'a, T> where Box<T>: Deserialize<'de>, { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { Deserialize::deserialize(deserializer).map(Ptr::Owned) } } } Cargo dependencies:
serde = { version = "1.0", features = ["derive"] } serde_derive = "1.0" serde_yaml = "0.7.5" serde_json = "1.0" Update:
I have had partial success getting the Struct out with:
let resulta: Outer<ExternalStructA> = serde_yaml::from_str(a).unwrap(); match resulta.ptr { Ptr::Owned(e) => {assert_eq!(1, e.x);}, Ptr::Ref(e) => {println!("error")}, Ptr::Owned(_) => {println!("error")} }; } But when I try implement this as a function using generic typing, I get lots of errors, the main being:
the trait `for<'de> tests::_IMPL_DESERIALIZE_FOR_ExternalStructA::_serde::Deserialize<'de>` is not implemented for `T` Non-Working code added to mod generics_yaml_deserializer
fn readfile<T>(filename: String) -> Result<Box<T>, Box<std::error::Error>> { let f = std::fs::File::open(filename)?; let config_data: Outer<T> = serde_yaml::from_reader(f)?; Ok(Box::new(config_data)) } fn readconfig<T>(filename: String) -> Result<Box<T>, &'static str> { // read the config file let config_data = readfile(filename); match config_data { Ok(e) => { Ok(Box::new(e)) }, Err(_) => { Err("nadda") } } }
StructAorStructB?