New config structs supports json again (the old style) for a little deprecation period

This commit is contained in:
Mathieu David 2017-05-19 00:56:37 +02:00
parent d3ae2eda56
commit 70383d0a25
12 changed files with 225 additions and 417 deletions

View File

@ -1,371 +0,0 @@
#![cfg(test)]
use std::path::Path;
use serde_json;
use book::bookconfig::*;
#[test]
fn it_parses_json_config() {
let text = r#"
{
"title": "mdBook Documentation",
"description": "Create book from markdown files. Like Gitbook but implemented in Rust",
"author": "Mathieu David"
}"#;
// TODO don't require path argument, take pwd
let mut config = BookConfig::new(Path::new("."));
config.parse_from_json_string(&text.to_string());
let mut expected = BookConfig::new(Path::new("."));
expected.title = "mdBook Documentation".to_string();
expected.author = "Mathieu David".to_string();
expected.description = "Create book from markdown files. Like Gitbook but implemented in Rust".to_string();
assert_eq!(format!("{:#?}", config), format!("{:#?}", expected));
}
#[test]
fn it_parses_toml_config() {
let text = r#"
title = "mdBook Documentation"
description = "Create book from markdown files. Like Gitbook but implemented in Rust"
author = "Mathieu David"
"#;
// TODO don't require path argument, take pwd
let mut config = BookConfig::new(Path::new("."));
config.parse_from_toml_string(&text.to_string());
let mut expected = BookConfig::new(Path::new("."));
expected.title = "mdBook Documentation".to_string();
expected.author = "Mathieu David".to_string();
expected.description = "Create book from markdown files. Like Gitbook but implemented in Rust".to_string();
assert_eq!(format!("{:#?}", config), format!("{:#?}", expected));
}
#[test]
fn it_parses_json_nested_array_to_toml() {
// Example from:
// toml-0.2.1/tests/valid/arrays-nested.json
let text = r#"
{
"nest": {
"type": "array",
"value": [
{"type": "array", "value": [
{"type": "string", "value": "a"}
]},
{"type": "array", "value": [
{"type": "string", "value": "b"}
]}
]
}
}"#;
let c: serde_json::Value = serde_json::from_str(&text).unwrap();
let result = json_object_to_btreemap(&c.as_object().unwrap());
let expected = r#"{
"nest": Table(
{
"type": String(
"array"
),
"value": Array(
[
Table(
{
"type": String(
"array"
),
"value": Array(
[
Table(
{
"type": String(
"string"
),
"value": String(
"a"
)
}
)
]
)
}
),
Table(
{
"type": String(
"array"
),
"value": Array(
[
Table(
{
"type": String(
"string"
),
"value": String(
"b"
)
}
)
]
)
}
)
]
)
}
)
}"#;
assert_eq!(format!("{:#?}", result), expected);
}
#[test]
fn it_parses_json_arrays_to_toml() {
// Example from:
// toml-0.2.1/tests/valid/arrays.json
let text = r#"
{
"ints": {
"type": "array",
"value": [
{"type": "integer", "value": "1"},
{"type": "integer", "value": "2"},
{"type": "integer", "value": "3"}
]
},
"floats": {
"type": "array",
"value": [
{"type": "float", "value": "1.1"},
{"type": "float", "value": "2.1"},
{"type": "float", "value": "3.1"}
]
},
"strings": {
"type": "array",
"value": [
{"type": "string", "value": "a"},
{"type": "string", "value": "b"},
{"type": "string", "value": "c"}
]
},
"dates": {
"type": "array",
"value": [
{"type": "datetime", "value": "1987-07-05T17:45:00Z"},
{"type": "datetime", "value": "1979-05-27T07:32:00Z"},
{"type": "datetime", "value": "2006-06-01T11:00:00Z"}
]
}
}"#;
let c: serde_json::Value = serde_json::from_str(&text).unwrap();
let result = json_object_to_btreemap(&c.as_object().unwrap());
let expected = r#"{
"dates": Table(
{
"type": String(
"array"
),
"value": Array(
[
Table(
{
"type": String(
"datetime"
),
"value": String(
"1987-07-05T17:45:00Z"
)
}
),
Table(
{
"type": String(
"datetime"
),
"value": String(
"1979-05-27T07:32:00Z"
)
}
),
Table(
{
"type": String(
"datetime"
),
"value": String(
"2006-06-01T11:00:00Z"
)
}
)
]
)
}
),
"floats": Table(
{
"type": String(
"array"
),
"value": Array(
[
Table(
{
"type": String(
"float"
),
"value": String(
"1.1"
)
}
),
Table(
{
"type": String(
"float"
),
"value": String(
"2.1"
)
}
),
Table(
{
"type": String(
"float"
),
"value": String(
"3.1"
)
}
)
]
)
}
),
"ints": Table(
{
"type": String(
"array"
),
"value": Array(
[
Table(
{
"type": String(
"integer"
),
"value": String(
"1"
)
}
),
Table(
{
"type": String(
"integer"
),
"value": String(
"2"
)
}
),
Table(
{
"type": String(
"integer"
),
"value": String(
"3"
)
}
)
]
)
}
),
"strings": Table(
{
"type": String(
"array"
),
"value": Array(
[
Table(
{
"type": String(
"string"
),
"value": String(
"a"
)
}
),
Table(
{
"type": String(
"string"
),
"value": String(
"b"
)
}
),
Table(
{
"type": String(
"string"
),
"value": String(
"c"
)
}
)
]
)
}
)
}"#;
assert_eq!(format!("{:#?}", result), expected);
}
#[test]
fn it_fetches_google_analytics_from_toml() {
let text = r#"
title = "mdBook Documentation"
description = "Create book from markdown files. Like Gitbook but implemented in Rust"
author = "Mathieu David"
google_analytics_id = "123456"
"#;
let mut config = BookConfig::new(Path::new("."));
config.parse_from_toml_string(&text.to_string());
let mut expected = BookConfig::new(Path::new("."));
expected.title = "mdBook Documentation".to_string();
expected.author = "Mathieu David".to_string();
expected.description = "Create book from markdown files. Like Gitbook but implemented in Rust".to_string();
expected.google_analytics = Some("123456".to_string());
assert_eq!(format!("{:#?}", config), format!("{:#?}", expected));
}

View File

@ -1,7 +1,4 @@
pub mod bookitem;
pub mod bookconfig;
pub mod bookconfig_test;
pub use self::bookitem::{BookItem, BookItems};
@ -18,6 +15,7 @@ use renderer::{Renderer, HtmlHandlebars};
use config::{BookConfig, HtmlConfig};
use config::tomlconfig::TomlConfig;
use config::jsonconfig::JsonConfig;
pub struct MDBook {
@ -45,7 +43,7 @@ impl MDBook {
/// # use mdbook::MDBook;
/// # use std::path::Path;
/// # fn main() {
/// let book = MDBook::new("root_dir");
/// let book = MDBook::new(Path::new("root_dir"));
/// # }
/// ```
///
@ -331,7 +329,13 @@ impl MDBook {
let parsed_config = TomlConfig::from_toml(&content)?;
self.config.fill_from_tomlconfig(parsed_config);
} else if json.exists() {
unimplemented!();
warn!("The JSON configuration file is deprecated, please use the TOML configuration.");
let mut file = File::open(json)?;
let mut content = String::new();
file.read_to_string(&mut content)?;
let parsed_config = JsonConfig::from_json(&content)?;
self.config.fill_from_jsonconfig(parsed_config);
}
Ok(self)

View File

@ -2,6 +2,7 @@ use std::path::{PathBuf, Path};
use super::HtmlConfig;
use super::tomlconfig::TomlConfig;
use super::jsonconfig::JsonConfig;
/// Configuration struct containing all the configuration options available in mdBook.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
@ -80,32 +81,7 @@ impl BookConfig {
pub fn from_tomlconfig<T: Into<PathBuf>>(root: T, tomlconfig: TomlConfig) -> Self {
let root = root.into();
let mut config = BookConfig::new(&root);
if let Some(s) = tomlconfig.source {
config.set_source(s);
}
if let Some(t) = tomlconfig.title {
config.set_title(t);
}
if let Some(d) = tomlconfig.description {
config.set_description(d);
}
if let Some(a) = tomlconfig.authors {
config.set_authors(a);
}
if let Some(a) = tomlconfig.author {
config.set_authors(vec![a]);
}
if let Some(tomlhtmlconfig) = tomlconfig.output.and_then(|o| o.html) {
let mut htmlconfig = config.get_mut_html_config().expect("We just created a new config and it creates a default HtmlConfig");
htmlconfig.fill_from_tomlconfig(&root, tomlhtmlconfig);
}
config.fill_from_tomlconfig(tomlconfig);
config
}
@ -141,6 +117,52 @@ impl BookConfig {
self
}
/// The JSON configuration file is **deprecated** and should not be used anymore.
/// Please, migrate to the TOML configuration file.
pub fn from_jsonconfig<T: Into<PathBuf>>(root: T, jsonconfig: JsonConfig) -> Self {
let root = root.into();
let mut config = BookConfig::new(&root);
config.fill_from_jsonconfig(jsonconfig);
config
}
/// The JSON configuration file is **deprecated** and should not be used anymore.
/// Please, migrate to the TOML configuration file.
pub fn fill_from_jsonconfig(&mut self, jsonconfig: JsonConfig) -> &mut Self {
if let Some(s) = jsonconfig.src {
self.set_source(s);
}
if let Some(t) = jsonconfig.title {
self.set_title(t);
}
if let Some(d) = jsonconfig.description {
self.set_description(d);
}
if let Some(a) = jsonconfig.author {
self.set_authors(vec![a]);
}
if let Some(d) = jsonconfig.dest {
let root = self.get_root().to_owned();
if let Some(htmlconfig) = self.get_mut_html_config() {
htmlconfig.set_destination(&root, &d);
}
}
if let Some(d) = jsonconfig.theme_path {
let root = self.get_root().to_owned();
if let Some(htmlconfig) = self.get_mut_html_config() {
htmlconfig.set_theme(&root, &d);
}
}
self
}
pub fn set_root<T: Into<PathBuf>>(&mut self, root: T) -> &mut Self {
self.root = root.into();
self

View File

@ -85,4 +85,8 @@ impl HtmlConfig {
self
}
pub fn get_google_analytics_id(&self) -> Option<String> {
self.google_analytics.clone()
}
}

43
src/config/jsonconfig.rs Normal file
View File

@ -0,0 +1,43 @@
extern crate serde_json;
use std::path::PathBuf;
/// The JSON configuration is **deprecated** and will be removed in the near future.
/// Please migrate to the TOML configuration.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct JsonConfig {
pub src: Option<PathBuf>,
pub dest: Option<PathBuf>,
pub title: Option<String>,
pub author: Option<String>,
pub description: Option<String>,
pub theme_path: Option<PathBuf>,
pub google_analytics: Option<String>,
}
/// Returns a JsonConfig from a JSON string
///
/// ```
/// # use mdbook::config::jsonconfig::JsonConfig;
/// # use std::path::PathBuf;
/// let json = r#"{
/// "title": "Some title",
/// "dest": "htmlbook"
/// }"#;
///
/// let config = JsonConfig::from_json(&json).expect("Should parse correctly");
/// assert_eq!(config.title, Some(String::from("Some title")));
/// assert_eq!(config.dest, Some(PathBuf::from("htmlbook")));
/// ```
impl JsonConfig {
pub fn from_json(input: &str) -> Result<Self, String> {
let config: JsonConfig = serde_json::from_str(input)
.map_err(|e| format!("Could not parse JSON: {}", e))?;
return Ok(config);
}
}

View File

@ -1,7 +1,9 @@
pub mod bookconfig;
pub mod htmlconfig;
pub mod tomlconfig;
pub mod jsonconfig;
// Re-export the config structs
pub use self::bookconfig::BookConfig;
pub use self::htmlconfig::HtmlConfig;
pub use self::tomlconfig::TomlConfig;

View File

@ -18,6 +18,7 @@ pub struct TomlOutputConfig {
pub html: Option<TomlHtmlConfig>,
}
#[serde(rename_all = "kebab-case")]
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct TomlHtmlConfig {
pub destination: Option<PathBuf>,

View File

@ -24,12 +24,13 @@
//! use std::path::Path;
//!
//! fn main() {
//! let mut book = MDBook::new(Path::new("my-book")) // Path to root
//! .set_src(Path::new("src")) // Path from root to source directory
//! .set_dest(Path::new("book")) // Path from root to output directory
//! .read_config(); // Parse book.json file for configuration
//! let mut book = MDBook::new(Path::new("my-book")) // Path to root
//! .with_source(Path::new("src")) // Path from root to source directory
//! .with_destination(Path::new("book")) // Path from root to output directory
//! .read_config() // Parse book.json file for configuration
//! .expect("I don't handle the error for the configuration file, but you should!");
//!
//! book.build().unwrap(); // Render the book
//! book.build().unwrap(); // Render the book
//! }
//! ```
//!

View File

@ -33,7 +33,7 @@ impl Renderer for HtmlHandlebars {
let mut handlebars = Handlebars::new();
// Load theme
let theme = theme::Theme::new(book.get_theme_path().expect("If the HTML renderer is called, one would assume the HtmlConfig is set..."));
let theme = theme::Theme::new(book.get_theme_path());
// Register template
debug!("[*]: Register handlebars template");
@ -53,7 +53,7 @@ impl Renderer for HtmlHandlebars {
// Check if dest directory exists
debug!("[*]: Check if destination directory exists");
if fs::create_dir_all(book.get_destination().expect("If the HTML renderer is called, one would assume the HtmlConfig is set...")).is_err() {
if fs::create_dir_all(book.get_destination().expect("If the HTML renderer is called, one would assume the HtmlConfig is set... (2)")).is_err() {
return Err(Box::new(io::Error::new(io::ErrorKind::Other,
"Unexpected error when constructing destination path")));
}
@ -119,7 +119,7 @@ impl Renderer for HtmlHandlebars {
let _source = File::open(
book.get_destination()
.expect("If the HTML renderer is called, one would assume the HtmlConfig is set...")
.expect("If the HTML renderer is called, one would assume the HtmlConfig is set... (3)")
.join(&ch.path.with_extension("html"))
)?.read_to_string(&mut content);
@ -136,7 +136,7 @@ impl Renderer for HtmlHandlebars {
info!("[*] Creating index.html from {:?} ✓",
book.get_destination()
.expect("If the HTML renderer is called, one would assume the HtmlConfig is set...")
.expect("If the HTML renderer is called, one would assume the HtmlConfig is set... (4)")
.join(&ch.path.with_extension("html"))
);
index = false;
@ -191,10 +191,9 @@ impl Renderer for HtmlHandlebars {
utils::fs::copy_files_except_ext(
book.get_source(),
book.get_destination()
.expect("If the HTML renderer is called, one would assume the HtmlConfig is set..."), true, &["md"]
.expect("If the HTML renderer is called, one would assume the HtmlConfig is set... (5)"), true, &["md"]
)?;
Ok(())
}
}

View File

@ -1,4 +1,4 @@
use std::path::Path;
use std::path::PathBuf;
use std::fs::File;
use std::io::Read;
@ -42,7 +42,7 @@ pub struct Theme {
}
impl Theme {
pub fn new(src: &Path) -> Self {
pub fn new(src: Option<&PathBuf>) -> Self {
// Default theme
let mut theme = Theme {
@ -58,10 +58,12 @@ impl Theme {
};
// Check if the given path exists
if !src.exists() || !src.is_dir() {
if src.is_none() || !src.unwrap().exists() || !src.unwrap().is_dir() {
return theme;
}
let src = src.unwrap();
// Check for individual files if they exist
// index.hbs

87
tests/jsonconfig.rs Normal file
View File

@ -0,0 +1,87 @@
extern crate mdbook;
use mdbook::config::BookConfig;
use mdbook::config::jsonconfig::JsonConfig;
use std::path::PathBuf;
// Tests that the `title` key is correcly parsed in the TOML config
#[test]
fn from_json_source() {
let json = r#"{
"src": "source"
}"#;
let parsed = JsonConfig::from_json(&json).expect("This should parse");
let config = BookConfig::from_jsonconfig("root", parsed);
assert_eq!(config.get_source(), PathBuf::from("root/source"));
}
// Tests that the `title` key is correcly parsed in the TOML config
#[test]
fn from_json_title() {
let json = r#"{
"title": "Some title"
}"#;
let parsed = JsonConfig::from_json(&json).expect("This should parse");
let config = BookConfig::from_jsonconfig("root", parsed);
assert_eq!(config.get_title(), "Some title");
}
// Tests that the `description` key is correcly parsed in the TOML config
#[test]
fn from_json_description() {
let json = r#"{
"description": "This is a description"
}"#;
let parsed = JsonConfig::from_json(&json).expect("This should parse");
let config = BookConfig::from_jsonconfig("root", parsed);
assert_eq!(config.get_description(), "This is a description");
}
// Tests that the `author` key is correcly parsed in the TOML config
#[test]
fn from_json_author() {
let json = r#"{
"author": "John Doe"
}"#;
let parsed = JsonConfig::from_json(&json).expect("This should parse");
let config = BookConfig::from_jsonconfig("root", parsed);
assert_eq!(config.get_authors(), &[String::from("John Doe")]);
}
// Tests that the `output.html.destination` key is correcly parsed in the TOML config
#[test]
fn from_json_destination() {
let json = r#"{
"dest": "htmlbook"
}"#;
let parsed = JsonConfig::from_json(&json).expect("This should parse");
let config = BookConfig::from_jsonconfig("root", parsed);
let htmlconfig = config.get_html_config().expect("There should be an HtmlConfig");
assert_eq!(htmlconfig.get_destination(), PathBuf::from("root/htmlbook"));
}
// Tests that the `output.html.theme` key is correcly parsed in the TOML config
#[test]
fn from_json_output_html_theme() {
let json = r#"{
"theme_path": "theme"
}"#;
let parsed = JsonConfig::from_json(&json).expect("This should parse");
let config = BookConfig::from_jsonconfig("root", parsed);
let htmlconfig = config.get_html_config().expect("There should be an HtmlConfig");
assert_eq!(htmlconfig.get_theme().expect("the theme key was provided"), &PathBuf::from("root/theme"));
}

View File

@ -84,5 +84,19 @@ fn from_toml_output_html_theme() {
let htmlconfig = config.get_html_config().expect("There should be an HtmlConfig");
assert_eq!(htmlconfig.get_theme().expect("the theme key was provided"), &PathBuf::from("root/src/theme"));
assert_eq!(htmlconfig.get_theme().expect("the theme key was provided"), &PathBuf::from("root/theme"));
}
// Tests that the `output.html.google-analytics` key is correcly parsed in the TOML config
#[test]
fn from_toml_output_html_google_analytics() {
let toml = r#"[output.html]
google-analytics = "123456""#;
let parsed = TomlConfig::from_toml(&toml).expect("This should parse");
let config = BookConfig::from_tomlconfig("root", parsed);
let htmlconfig = config.get_html_config().expect("There should be an HtmlConfig");
assert_eq!(htmlconfig.get_google_analytics_id().expect("the google-analytics key was provided"), String::from("123456"));
}