help-rust/README.md

167 lines
4.3 KiB
Markdown
Raw 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
I think, a big part of my difficulty is that the file is essentially a recursive hash-map, but the `actions` key is _one_ strictly-defined structure, while all _other_ keys must _together_ become a single hash-map.
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-17 20:41:58 +01:00
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-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>
}
#[derive(Deserialize)]
#[serde(untagged)]
pub enum ConfigPart {
Actions(Vec<Chain>),
Option(HashMap<String, Value>)
}
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![];
while let v = seq.next_element() {
list.append(Value.deserialize(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> {
}*/
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error> where D: Deserializer<'de> {
deserializer.(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-17 20:41:58 +01:00
1. Im far from sure, that `visit_seq` is correct.
2. I dont know how to finish the last block of code.
3. Im rather sure, at this point I wont know how to handle the “untagged enum”…
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