2019-12-17 20:41:58 +01:00
## My issue
I want to deserialize data into Rust types using serde.
The data format already exists and I want to remain compatible. It is currently JSON, but I want to specify deserializers independently of the format (so that later I can use eg. YAML).
My file format is documented here: https://yalis.fr/git/yves/pyruse/src/branch/master/doc/conffile.md
In module `common` , I have:
```rust
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum Value {
Bool(bool),
Str(String),
Int(isize),
Date(DateTime< chrono::Utc > ),
Map(HashMap< String , Value > ),
List(Vec< Value > )
}
```
NOTE: No data from the file will ever become a `Date` variant.
2019-11-17 16:50:48 +01:00
2019-11-17 15:30:48 +01:00
In module `modules` , I have:
```rust
2019-12-17 20:41:58 +01:00
pub type ModuleArgs = HashMap< String , Value > ;
```
The deserialization process is programmed in the `config` module. Here is what I first described:
```rust
pub struct Config {
actions: Vec< Chain > ,
options: HashMap< String , Value >
2019-11-17 15:30:48 +01:00
}
2019-12-17 20:41:58 +01:00
pub struct Chain {
name: String,
steps: Vec< Step >
2019-11-24 18:59:44 +01:00
}
2019-11-17 15:30:48 +01:00
2019-12-17 20:41:58 +01:00
pub struct Step {
module_name: String,
args: ModuleArgs,
then_dest: Option< String > ,
else_dest: Option< String >
}
```
(Subsidiary question: If `args` above is not specified in the file, is it OK to want the `args` field to be an empty Hashmap, or is it worth the extra work to make it an `Option<ModuleArgs>` ?)
I tried to follow the documentation, and misc. web pages, and ended up with that, so far:
```rust
use serde::de::{self,Deserializer,MapAccess,SeqAccess,Visitor};
use serde::Deserialize;
use std::collections::HashMap;
use std::fmt;
use crate::common::Value;
use crate::modules::ModuleArgs;
2019-11-17 15:30:48 +01:00
2019-12-18 08:13:25 +01:00
#[derive(Deserialize)]
2019-12-17 20:41:58 +01:00
pub struct Config {
actions: Vec< Chain > ,
2019-12-18 08:13:25 +01:00
#[serde(flatten)]
2019-12-17 20:41:58 +01:00
options: HashMap< String , Value >
2019-11-17 15:30:48 +01:00
}
2019-12-17 20:41:58 +01:00
pub struct Chain {
name: String,
steps: Vec< Step >
2019-11-17 15:30:48 +01:00
}
2019-12-17 20:41:58 +01:00
pub struct Step {
module_name: String,
args: ModuleArgs,
then_dest: Option< String > ,
else_dest: Option< String >
}
struct ValueVisitor;
impl< 'de> Visitor< 'de> for ValueVisitor {
type Value = Value;
fn expecting(& self, formatter: & mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a boolean, string, integer, array or object")
}
fn visit_bool< E > (self, v: bool) -> Result< Self::Value , E > where E: de::Error {
Ok(Value::Bool(v))
}
fn visit_i8< E > (self, v: i8) -> Result< Self::Value , E > where E: de::Error {
Ok(Value::Int(v as isize))
}
// … and so on …
fn visit_u8< E > (self, v: u8) -> Result< Self::Value , E > where E: de::Error {
Ok(Value::Int(v as isize))
}
// … and so on …
fn visit_f32< E > (self, v: f32) -> Result< Self::Value , E > where E: de::Error {
Ok(Value::Int(v as isize))
}
// … and so on …
fn visit_char< E > (self, v: char) -> Result< Self::Value , E > where E: de::Error {
Ok(Value::Str(v.to_string()))
}
// … and so on …
fn visit_bytes< E > (self, v: & [u8]) -> Result< Self::Value , E > where E: de::Error {
Ok(Value::Str(std::str::from_utf8(v).expect("Strings in the configuration must be UTF-8").to_string()))
}
// … and so on …
fn visit_seq< A > (self, seq: A) -> Result< Self::Value , A::Error > where A: SeqAccess< 'de> {
2019-12-18 08:13:25 +01:00
let list = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(v) = seq.next_element()? {
2019-12-19 19:21:25 +01:00
list.append(v);
2019-11-24 18:59:44 +01:00
}
2019-12-17 20:41:58 +01:00
Ok(Value::List(list))
}
2019-12-18 08:13:25 +01:00
fn visit_map< A > (self, map: A) -> Result< Self::Value , A::Error > where A: MapAccess< 'de> {
let map = HashMap::with_capacity(map.size_hint().unwrap_or(0));
while let Some((k, v)) = map.next_entry()? {
map.insert(k, v);
}
Ok(Value::Map(map))
}
2019-12-17 20:41:58 +01:00
}
impl< 'de> Deserialize< 'de> for Value {
fn deserialize< D > (deserializer: D) -> Result< Value , D::Error > where D: Deserializer< 'de> {
2019-12-19 19:21:25 +01:00
deserializer.deserialize_any(ValueVisitor)
2019-11-24 18:59:44 +01:00
}
2019-11-17 15:30:48 +01:00
}
```
2019-12-17 20:41:58 +01:00
And here, I’ m stuck…
2019-11-17 16:16:25 +01:00
2019-12-19 19:21:25 +01:00
1. Both recursive visitors (`visit_seq` and `visit_map` ) fail to compile. Basically, I just cannot find anywhere on the Internet how to deserialize a sub-structure from a Visitor…
2019-11-17 16:16:25 +01:00
2019-12-17 20:41:58 +01:00
Here is the documentation I found, where I try to find inspiration:
2019-11-17 16:16:25 +01:00
2019-12-17 20:41:58 +01:00
* https://serde.rs/impl-deserialize.html (obviously)
* https://serde.rs/enum-representations.html
* https://github.com/serde-rs/json/issues/144
* https://github.com/serde-rs/serde/issues/1470
* https://github.com/serde-rs/serde/issues/1131
* https://github.com/serde-rs/serde/issues/1364
* https://stackoverflow.com/a/48293152/6751571
2019-12-18 08:13:25 +01:00
* https://play.rust-lang.org/?version=stable& mode=debug& edition=2018& gist=31881ff3984a8600861f2f4ee32faf23 (Thank you GreenJello)