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",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"toml",
|
"toml",
|
||||||
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -69,3 +70,9 @@ name = "unicode-xid"
|
|||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
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 = "1"
|
||||||
serde_derive = "1"
|
serde_derive = "1"
|
||||||
toml = "0.5.8"
|
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::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 {
|
struct Config {
|
||||||
|
storage: Storage,
|
||||||
sites: HashMap<String, Site>,
|
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)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
struct Site {
|
struct Site {
|
||||||
url: String,
|
url: String,
|
||||||
title: String,
|
title: String,
|
||||||
hover: Option<String>,
|
hover: Option<String>,
|
||||||
main_image: String,
|
#[serde(deserialize_with = "string_or_struct")]
|
||||||
|
main_image: Extractor,
|
||||||
alternate_image: Option<String>,
|
alternate_image: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<(), Box<std::error::Error>> {
|
fn main() -> Result<(), Box<std::error::Error>> {
|
||||||
let c = Config {
|
let c = Config {
|
||||||
|
storage: Storage::Filesystem {
|
||||||
|
path: "/path/to/data".into(),
|
||||||
|
},
|
||||||
sites: vec![
|
sites: vec![
|
||||||
(
|
(
|
||||||
"xkcd".to_string(),
|
"xkcd".to_string(),
|
||||||
Site {
|
Site {
|
||||||
url: "https://xkcd.com/1/".to_string(),
|
url: "https://xkcd.com/1/".to_string(),
|
||||||
title: "title".to_string(),
|
title: "title".to_owned(),
|
||||||
hover: None,
|
hover: None,
|
||||||
main_image: "img".to_string(),
|
main_image: Extractor {
|
||||||
|
selector: "img".to_owned(),
|
||||||
|
attribute: Some("src".to_owned()),
|
||||||
|
},
|
||||||
alternate_image: None,
|
alternate_image: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -31,9 +126,12 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
|||||||
"sinfest".to_string(),
|
"sinfest".to_string(),
|
||||||
Site {
|
Site {
|
||||||
url: "https://sinfest.net/view.php?date=2000-01-17".to_string(),
|
url: "https://sinfest.net/view.php?date=2000-01-17".to_string(),
|
||||||
title: "title".to_string(),
|
title: "title".to_owned(),
|
||||||
hover: None,
|
hover: None,
|
||||||
main_image: "img".to_string(),
|
main_image: Extractor {
|
||||||
|
selector: "img".to_owned(),
|
||||||
|
attribute: Some("src".to_owned()),
|
||||||
|
},
|
||||||
alternate_image: None,
|
alternate_image: None,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -41,11 +139,24 @@ fn main() -> Result<(), Box<std::error::Error>> {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.collect(),
|
.collect(),
|
||||||
};
|
};
|
||||||
|
println!("About to serialize");
|
||||||
let s = toml::to_string(&c)?;
|
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
|
// 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. "{:?}").
|
// formatting (i.e. "{}") instead of debug formating (i.e. "{:?}").
|
||||||
let c = toml::from_str(&s)?;
|
let c: Config = toml::from_str(&s)?;
|
||||||
println!("c {:?}", c);
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user