Added some basic deserializing tests and helpers
This commit is contained in:
parent
6059883229
commit
1d22a9a040
|
@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
use toml::{self, Value};
|
use toml::{self, Value};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use errors::*;
|
use errors::*;
|
||||||
|
|
||||||
|
@ -20,7 +21,7 @@ pub use self::tomlconfig::TomlConfig;
|
||||||
|
|
||||||
|
|
||||||
/// The overall configuration object for MDBook.
|
/// The overall configuration object for MDBook.
|
||||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Metadata about the book.
|
/// Metadata about the book.
|
||||||
|
@ -58,16 +59,42 @@ impl Config {
|
||||||
/// This is for compatibility only. It will be removed completely once the
|
/// This is for compatibility only. It will be removed completely once the
|
||||||
/// rendering and plugin system is established.
|
/// rendering and plugin system is established.
|
||||||
pub fn html_config(&self) -> Option<HtmlConfig_> {
|
pub fn html_config(&self) -> Option<HtmlConfig_> {
|
||||||
self.output
|
self.try_get_output("html").ok()
|
||||||
.get("html")
|
}
|
||||||
.and_then(|value| HtmlConfig_::from_toml(value).ok())
|
|
||||||
|
/// Try to get an output and deserialize it as a `T`.
|
||||||
|
pub fn try_get_output<'de, T: Deserialize<'de>, S: AsRef<str>>(&self, name: S) -> Result<T> {
|
||||||
|
get_deserialized(name, &self.output)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to get the configuration for a preprocessor, deserializing it as a
|
||||||
|
/// `T`.
|
||||||
|
pub fn try_get_preprocessor<'de, T: Deserialize<'de>, S: AsRef<str>>(&self, name: S) -> Result<T> {
|
||||||
|
get_deserialized(name, &self.preprocess)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to get the configuration for a postprocessor, deserializing it as a
|
||||||
|
/// `T`.
|
||||||
|
pub fn try_get_postprocessor<'de, T: Deserialize<'de>, S: AsRef<str>>(&self, name: S) -> Result<T> {
|
||||||
|
get_deserialized(name, &self.postprocess)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience function to load a value from some table then deserialize it.
|
||||||
|
fn get_deserialized<'de, T: Deserialize<'de>, S: AsRef<str>>(name: S, table: &BTreeMap<String, Value>) -> Result<T> {
|
||||||
|
match table.get(name.as_ref()) {
|
||||||
|
Some(output) => output
|
||||||
|
.clone()
|
||||||
|
.try_into()
|
||||||
|
.chain_err(|| "Couldn't deserialize the value"),
|
||||||
|
None => bail!("Key Not Found"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Configuration options which are specific to the book and required for
|
/// Configuration options which are specific to the book and required for
|
||||||
/// loading it from disk.
|
/// loading it from disk.
|
||||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(default, rename_all = "kebab-case")]
|
#[serde(default, rename_all = "kebab-case")]
|
||||||
pub struct BookConfig_ {
|
pub struct BookConfig_ {
|
||||||
/// The book's title.
|
/// The book's title.
|
||||||
|
@ -84,7 +111,20 @@ pub struct BookConfig_ {
|
||||||
pub multilingual: bool,
|
pub multilingual: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
|
impl Default for BookConfig_ {
|
||||||
|
fn default() -> BookConfig_ {
|
||||||
|
BookConfig_ {
|
||||||
|
title: None,
|
||||||
|
authors: Vec::new(),
|
||||||
|
description: None,
|
||||||
|
src: PathBuf::from("src"),
|
||||||
|
build_dir: PathBuf::from("build"),
|
||||||
|
multilingual: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||||
#[serde(default, rename_all = "kebab-case")]
|
#[serde(default, rename_all = "kebab-case")]
|
||||||
pub struct HtmlConfig_ {
|
pub struct HtmlConfig_ {
|
||||||
pub theme: Option<PathBuf>,
|
pub theme: Option<PathBuf>,
|
||||||
|
@ -96,14 +136,78 @@ pub struct HtmlConfig_ {
|
||||||
pub playpen: Playpen,
|
pub playpen: Playpen,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HtmlConfig_ {
|
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||||
pub fn from_toml(value: &Value) -> Result<HtmlConfig_> {
|
pub struct Playpen;
|
||||||
value
|
|
||||||
.clone()
|
|
||||||
.try_into()
|
#[cfg(test)]
|
||||||
.chain_err(|| "Unable to deserialize the HTML config")
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_a_complex_config_file() {
|
||||||
|
let src = r#"
|
||||||
|
[book]
|
||||||
|
title = "Some Book"
|
||||||
|
authors = ["Michael-F-Bryan <michaelfbryan@gmail.com>"]
|
||||||
|
description = "A completely useless book"
|
||||||
|
multilingual = true
|
||||||
|
src = "source"
|
||||||
|
build-dir = "outputs"
|
||||||
|
|
||||||
|
[output.html]
|
||||||
|
curly-quotes = true
|
||||||
|
google-analytics = "123456"
|
||||||
|
additional-css = ["./foo/bar/baz.css"]
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let book_should_be = BookConfig_ {
|
||||||
|
title: Some(String::from("Some Book")),
|
||||||
|
authors: vec![String::from("Michael-F-Bryan <michaelfbryan@gmail.com>")],
|
||||||
|
description: Some(String::from("A completely useless book")),
|
||||||
|
multilingual: true,
|
||||||
|
src: PathBuf::from("source"),
|
||||||
|
build_dir: PathBuf::from("outputs"),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let html_should_be = HtmlConfig_ {
|
||||||
|
curly_quotes: true,
|
||||||
|
google_analytics: Some(String::from("123456")),
|
||||||
|
additional_css: vec![PathBuf::from("./foo/bar/baz.css")],
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let got = Config::from_str(src).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(got.book, book_should_be);
|
||||||
|
assert_eq!(got.html_config().unwrap(), html_should_be);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn load_arbitrary_output_type() {
|
||||||
|
#[derive(Debug, Deserialize, PartialEq)]
|
||||||
|
struct RandomOutput {
|
||||||
|
foo: u32,
|
||||||
|
bar: String,
|
||||||
|
baz: Vec<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = r#"
|
||||||
|
[output.random]
|
||||||
|
foo = 5
|
||||||
|
bar = "Hello World"
|
||||||
|
baz = [true, true, false]
|
||||||
|
"#;
|
||||||
|
|
||||||
|
let should_be = RandomOutput {
|
||||||
|
foo: 5,
|
||||||
|
bar: String::from("Hello World"),
|
||||||
|
baz: vec![true, true, false],
|
||||||
|
};
|
||||||
|
|
||||||
|
let cfg = Config::from_str(src).unwrap();
|
||||||
|
let got: RandomOutput = cfg.try_get_output("random").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(got, should_be);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Default, PartialEq, Serialize, Deserialize)]
|
|
||||||
pub struct Playpen;
|
|
||||||
|
|
Loading…
Reference in New Issue