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")
}
}
}
StructA
orStructB
? – 9000