Testing enums and custom string_or_struct decoders.
This commit is contained in:
parent
33bf1dc1b3
commit
344ca0b713
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -25,6 +25,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"toml",
|
||||
"void",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -69,3 +70,9 @@ name = "unicode-xid"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
|
||||
@ -10,3 +10,4 @@ edition = "2018"
|
||||
serde = "1"
|
||||
serde_derive = "1"
|
||||
toml = "0.5.8"
|
||||
void = "1"
|
||||
|
||||
131
src/main.rs
131
src/main.rs
@ -1,29 +1,124 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::marker::PhantomData;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use void::Void;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
use serde::de::{self, MapAccess, Visitor};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(tag = "storage")]
|
||||
enum Storage {
|
||||
S3 { bucket: String },
|
||||
Filesystem { path: PathBuf },
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
struct Config {
|
||||
storage: Storage,
|
||||
sites: HashMap<String, Site>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Extractor {
|
||||
selector: String,
|
||||
attribute: Option<String>,
|
||||
}
|
||||
|
||||
// The `string_or_struct` function uses this impl to instantiate a `Build` if
|
||||
// the input file contains a string and not a struct. According to the
|
||||
// docker-compose.yml documentation, a string by itself represents a `Build`
|
||||
// with just the `context` field set.
|
||||
//
|
||||
// > `build` can be specified either as a string containing a path to the build
|
||||
// > context, or an object with the path specified under context and optionally
|
||||
// > dockerfile and args.
|
||||
impl FromStr for Extractor {
|
||||
// This implementation of `from_str` can never fail, so use the impossible
|
||||
// `Void` type as the error type.
|
||||
type Err = Void;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Extractor {
|
||||
selector: s.to_string(),
|
||||
attribute: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn string_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: Deserialize<'de> + FromStr<Err = Void>,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
// This is a Visitor that forwards string types to T's `FromStr` impl and
|
||||
// forwards map types to T's `Deserialize` impl. The `PhantomData` is to
|
||||
// keep the compiler from complaining about T being an unused generic type
|
||||
// parameter. We need T in order to know the Value type for the Visitor
|
||||
// impl.
|
||||
struct StringOrStruct<T>(PhantomData<fn() -> T>);
|
||||
|
||||
impl<'de, T> Visitor<'de> for StringOrStruct<T>
|
||||
where
|
||||
T: Deserialize<'de> + FromStr<Err = Void>,
|
||||
{
|
||||
type Value = T;
|
||||
|
||||
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||
formatter.write_str("string or map")
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<T, E>
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(FromStr::from_str(value).unwrap())
|
||||
}
|
||||
|
||||
fn visit_map<M>(self, map: M) -> Result<T, M::Error>
|
||||
where
|
||||
M: MapAccess<'de>,
|
||||
{
|
||||
// `MapAccessDeserializer` is a wrapper that turns a `MapAccess`
|
||||
// into a `Deserializer`, allowing it to be used as the input to T's
|
||||
// `Deserialize` implementation. T then deserializes itself using
|
||||
// the entries from the map visitor.
|
||||
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(StringOrStruct(PhantomData))
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
struct Site {
|
||||
url: String,
|
||||
title: String,
|
||||
hover: Option<String>,
|
||||
main_image: String,
|
||||
#[serde(deserialize_with = "string_or_struct")]
|
||||
main_image: Extractor,
|
||||
alternate_image: Option<String>,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<std::error::Error>> {
|
||||
let c = Config {
|
||||
storage: Storage::Filesystem {
|
||||
path: "/path/to/data".into(),
|
||||
},
|
||||
sites: vec![
|
||||
(
|
||||
"xkcd".to_string(),
|
||||
Site {
|
||||
url: "https://xkcd.com/1/".to_string(),
|
||||
title: "title".to_string(),
|
||||
title: "title".to_owned(),
|
||||
hover: None,
|
||||
main_image: "img".to_string(),
|
||||
main_image: Extractor {
|
||||
selector: "img".to_owned(),
|
||||
attribute: Some("src".to_owned()),
|
||||
},
|
||||
alternate_image: None,
|
||||
},
|
||||
),
|
||||
@ -31,9 +126,12 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
"sinfest".to_string(),
|
||||
Site {
|
||||
url: "https://sinfest.net/view.php?date=2000-01-17".to_string(),
|
||||
title: "title".to_string(),
|
||||
title: "title".to_owned(),
|
||||
hover: None,
|
||||
main_image: "img".to_string(),
|
||||
main_image: Extractor {
|
||||
selector: "img".to_owned(),
|
||||
attribute: Some("src".to_owned()),
|
||||
},
|
||||
alternate_image: None,
|
||||
},
|
||||
),
|
||||
@ -41,11 +139,24 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
||||
.into_iter()
|
||||
.collect(),
|
||||
};
|
||||
println!("About to serialize");
|
||||
let s = toml::to_string(&c)?;
|
||||
println!("s {}", s);
|
||||
println!("Serialized\n{}", s);
|
||||
// c is inferred as the unit type judging by the error you get when you print with Display
|
||||
// formatting (i.e. "{}") instead of debug formating (i.e. "{:?}").
|
||||
let c = toml::from_str(&s)?;
|
||||
println!("c {:?}", c);
|
||||
let c: Config = toml::from_str(&s)?;
|
||||
println!("Deserialized\n{:#?}", c);
|
||||
let c: Config = toml::from_str(
|
||||
r#"
|
||||
[storage]
|
||||
storage = "Filesystem"
|
||||
path = "/path/to/data"
|
||||
[sites.test]
|
||||
url = "http://google.com"
|
||||
title = "title"
|
||||
main_image = "img"
|
||||
"#,
|
||||
)?;
|
||||
println!("Hardcode\n{:#?}", c);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user