help-rust/README.md

164 lines
4.4 KiB
Markdown
Raw Permalink Normal View History

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
#[derive(Deserialize)]
2019-12-17 20:41:58 +01:00
pub struct Config {
actions: Vec<Chain>,
#[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> {
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))
}
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, Im 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
* https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=31881ff3984a8600861f2f4ee32faf23 (Thank you GreenJello)