help-rust/README.md

164 lines
4.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## 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.
In module `modules`, I have:
```rust
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>
}
pub struct Chain {
name: String,
steps: Vec<Step>
}
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;
#[derive(Deserialize)]
pub struct Config {
actions: Vec<Chain>,
#[serde(flatten)]
options: HashMap<String, Value>
}
pub struct Chain {
name: String,
steps: Vec<Step>
}
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()? {
list.append(v);
}
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))
}
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Value, D::Error> where D: Deserializer<'de> {
deserializer.deserialize_any(ValueVisitor)
}
}
```
And here, Im stuck…
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…
Here is the documentation I found, where I try to find inspiration:
* 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)