Skip to content

Parse toml #190

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Dec 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ exclude = [
]

[dependencies]
clap = "2.2.1"
handlebars = { version = "0.20.0", features = ["serde_type"] }
serde = "0.8.17"
serde_json = "0.8.3"
clap = "2.19.2"
handlebars = { version = "0.23.0", features = ["serde_type"] }
serde = "0.8"
serde_json = "0.8"
pulldown-cmark = "0.0.8"
log = "0.3"
env_logger = "0.3.4"
env_logger = "0.3"
toml = { version = "0.2", features = ["serde"] }

# Watch feature
notify = { version = "2.5.5", optional = true }
Expand All @@ -33,12 +34,10 @@ iron = { version = "0.4", optional = true }
staticfile = { version = "0.3", optional = true }
ws = { version = "0.5.1", optional = true}


# Tests
[dev-dependencies]
tempdir = "0.3.4"


[features]
default = ["output", "watch", "serve"]
debug = []
Expand Down
5 changes: 0 additions & 5 deletions book-example/book.json

This file was deleted.

3 changes: 3 additions & 0 deletions book-example/book.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
title = "mdBook Documentation"
description = "Create book from markdown files. Like Gitbook but implemented in Rust"
author = "Mathieu David"
2 changes: 2 additions & 0 deletions book-example/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Summary

[Introduction](misc/introduction.md)

- [mdBook](README.md)
- [Command Line Tool](cli/cli-tool.md)
- [init](cli/init.md)
Expand Down
2 changes: 1 addition & 1 deletion book-example/src/cli/test.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ mdBook supports a `test` command that will run all available tests in mdBook. At
- checking for unused files
- ...

In the future I would like the user to be able to enable / disable test from the `book.json` configuration file and support custom tests.
In the future I would like the user to be able to enable / disable test from the `book.toml` configuration file and support custom tests.

**How to use it:**
```bash
Expand Down
18 changes: 9 additions & 9 deletions book-example/src/format/config.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Configuration

You can configure the parameters for your book in the ***book.json*** file.
You can configure the parameters for your book in the ***book.toml*** file.

Here is an example of what a ***book.json*** file might look like:
We encourage using the TOML format, but JSON is also recognized and parsed.

```json
{
"title": "Example book",
"author": "Name",
"description": "The example book covers examples.",
"dest": "output/my-book"
}
Here is an example of what a ***book.toml*** file might look like:

```toml
title = "Example book"
author = "Name"
description = "The example book covers examples."
dest = "output/my-book"
```

#### Supported variables
Expand Down
2 changes: 1 addition & 1 deletion book-example/src/format/format.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ In this section you will learn how to:

- Structure your book correctly
- Format your `SUMMARY.md` file
- Configure your book using `book.json`
- Configure your book using `book.toml`
- Customize your theme
2 changes: 1 addition & 1 deletion book-example/src/format/theme/index-hbs.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ Here is a list of the properties that are exposed:

- ***language*** Language of the book in the form `en`. To use in <code class="language-html">\<html lang="{{ language }}"></code> for example.
At the moment it is hardcoded.
- ***title*** Title of the book, as specified in `book.json`
- ***title*** Title of the book, as specified in `book.toml`

- ***path*** Relative path to the original markdown file from the source directory
- ***content*** This is the rendered markdown.
Expand Down
2 changes: 1 addition & 1 deletion book-example/src/format/theme/syntax-highlighting.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Will render as
# }
```

**At the moment, this only works for code examples that are annotated with `rust`. Because it would collide with semantics of some programming languages. In the future, we want to make this configurable through the `book.json` so that everyone can benefit from it.**
**At the moment, this only works for code examples that are annotated with `rust`. Because it would collide with semantics of some programming languages. In the future, we want to make this configurable through the `book.toml` so that everyone can benefit from it.**


## Improve default theme
Expand Down
2 changes: 1 addition & 1 deletion book-example/src/lib/lib.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ 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
.read_config(); // Parse book.toml or book.json file for configuration

book.build().unwrap(); // Render the book
}
Expand Down
3 changes: 3 additions & 0 deletions book-example/src/misc/introduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Introduction

A frontmatter chapter.
182 changes: 131 additions & 51 deletions src/book/bookconfig.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
use serde_json;
extern crate toml;

use std::process::exit;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::collections::BTreeMap;
use std::str::FromStr;
use serde_json;

#[derive(Debug, Clone)]
pub struct BookConfig {
Expand All @@ -18,7 +23,6 @@ pub struct BookConfig {
multilingual: bool,
}


impl BookConfig {
pub fn new(root: &Path) -> Self {
BookConfig {
Expand All @@ -40,71 +44,117 @@ impl BookConfig {

debug!("[fn]: read_config");

// If the file does not exist, return early
let mut config_file = match File::open(root.join("book.json")) {
Ok(f) => f,
Err(_) => {
debug!("[*]: Failed to open {:?}", root.join("book.json"));
return self;
},
let read_file = |path: PathBuf| -> String {
let mut data = String::new();
let mut f: File = match File::open(&path) {
Ok(x) => x,
Err(_) => {
error!("[*]: Failed to open {:?}", &path);
exit(2);
}
};
if let Err(_) = f.read_to_string(&mut data) {
error!("[*]: Failed to read {:?}", &path);
exit(2);
}
data
};

debug!("[*]: Reading config");
let mut data = String::new();
// Read book.toml or book.json if exists

// Just return if an error occured.
// I would like to propagate the error, but I have to return `&self`
if let Err(_) = config_file.read_to_string(&mut data) {
return self;
if Path::new(root.join("book.toml").as_os_str()).exists() {

debug!("[*]: Reading config");
let data = read_file(root.join("book.toml"));
self.parse_from_toml_string(&data);

} else if Path::new(root.join("book.json").as_os_str()).exists() {

debug!("[*]: Reading config");
let data = read_file(root.join("book.json"));
self.parse_from_json_string(&data);

} else {
debug!("[*]: No book.toml or book.json was found, using defaults.");
}

// Convert to JSON
if let Ok(config) = serde_json::from_str::<serde_json::Value>(&data) {
// Extract data
self
}

let config = config.as_object().unwrap();
pub fn parse_from_toml_string(&mut self, data: &String) -> &mut Self {

debug!("[*]: Extracting data from config");
// Title, author, description
if let Some(a) = config.get("title") {
self.title = a.to_string().replace("\"", "")
}
if let Some(a) = config.get("author") {
self.author = a.to_string().replace("\"", "")
}
if let Some(a) = config.get("description") {
self.description = a.to_string().replace("\"", "")
let mut parser = toml::Parser::new(&data);

let config = match parser.parse() {
Some(x) => {x},
None => {
error!("[*]: Toml parse errors in book.toml: {:?}", parser.errors);
exit(2);
}
};

// Destination folder
if let Some(a) = config.get("dest") {
let mut dest = PathBuf::from(&a.to_string().replace("\"", ""));
self.parse_from_btreemap(&config);

// If path is relative make it absolute from the parent directory of src
if dest.is_relative() {
dest = self.get_root().join(&dest);
}
self.set_dest(&dest);
self
}

/// Parses the string to JSON and converts it to BTreeMap<String, toml::Value>.
pub fn parse_from_json_string(&mut self, data: &String) -> &mut Self {

let c: serde_json::Value = match serde_json::from_str(&data) {
Ok(x) => x,
Err(e) => {
error!("[*]: JSON parse errors in book.json: {:?}", e);
exit(2);
}
};

// Source folder
if let Some(a) = config.get("src") {
let mut src = PathBuf::from(&a.to_string().replace("\"", ""));
if src.is_relative() {
src = self.get_root().join(&src);
}
self.set_src(&src);
let config = json_object_to_btreemap(&c.as_object().unwrap());
self.parse_from_btreemap(&config);

self
}

pub fn parse_from_btreemap(&mut self, config: &BTreeMap<String, toml::Value>) -> &mut Self {

// Title, author, description
if let Some(a) = config.get("title") {
self.title = a.to_string().replace("\"", "");
}
if let Some(a) = config.get("author") {
self.author = a.to_string().replace("\"", "");
}
if let Some(a) = config.get("description") {
self.description = a.to_string().replace("\"", "");
}

// Destination folder
if let Some(a) = config.get("dest") {
let mut dest = PathBuf::from(&a.to_string().replace("\"", ""));

// If path is relative make it absolute from the parent directory of src
if dest.is_relative() {
dest = self.get_root().join(&dest);
}
self.set_dest(&dest);
}

// Theme path folder
if let Some(a) = config.get("theme_path") {
let mut theme_path = PathBuf::from(&a.to_string().replace("\"", ""));
if theme_path.is_relative() {
theme_path = self.get_root().join(&theme_path);
}
self.set_theme_path(&theme_path);
// Source folder
if let Some(a) = config.get("src") {
let mut src = PathBuf::from(&a.to_string().replace("\"", ""));
if src.is_relative() {
src = self.get_root().join(&src);
}
self.set_src(&src);
}

// Theme path folder
if let Some(a) = config.get("theme_path") {
let mut theme_path = PathBuf::from(&a.to_string().replace("\"", ""));
if theme_path.is_relative() {
theme_path = self.get_root().join(&theme_path);
}
self.set_theme_path(&theme_path);
}

self
Expand Down Expand Up @@ -146,3 +196,33 @@ impl BookConfig {
self
}
}

pub fn json_object_to_btreemap(json: &serde_json::Map<String, serde_json::Value>) -> BTreeMap<String, toml::Value> {
let mut config: BTreeMap<String, toml::Value> = BTreeMap::new();

for (key, value) in json.iter() {
config.insert(
String::from_str(key).unwrap(),
json_value_to_toml_value(value.to_owned())
);
}

config
}

pub fn json_value_to_toml_value(json: serde_json::Value) -> toml::Value {
match json {
serde_json::Value::Null => toml::Value::String("".to_string()),
serde_json::Value::Bool(x) => toml::Value::Boolean(x),
serde_json::Value::I64(x) => toml::Value::Integer(x),
serde_json::Value::U64(x) => toml::Value::Integer(x as i64),
serde_json::Value::F64(x) => toml::Value::Float(x),
serde_json::Value::String(x) => toml::Value::String(x),
serde_json::Value::Array(x) => {
toml::Value::Array(x.iter().map(|v| json_value_to_toml_value(v.to_owned())).collect())
},
serde_json::Value::Object(x) => {
toml::Value::Table(json_object_to_btreemap(&x))
},
}
}
Loading