2017-07-01 07:47:41 +00:00
|
|
|
#[macro_use]
|
|
|
|
extern crate serde_derive;
|
|
|
|
extern crate toml;
|
|
|
|
#[macro_use]
|
|
|
|
extern crate errors;
|
2017-10-30 20:55:14 +00:00
|
|
|
extern crate highlighting;
|
2017-08-07 11:38:13 +00:00
|
|
|
extern crate chrono;
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
extern crate globset;
|
2017-07-01 07:47:41 +00:00
|
|
|
|
|
|
|
use std::collections::HashMap;
|
2016-12-06 06:55:17 +00:00
|
|
|
use std::fs::File;
|
|
|
|
use std::io::prelude::*;
|
2017-08-24 23:38:03 +00:00
|
|
|
use std::path::{Path, PathBuf};
|
2016-12-06 05:51:33 +00:00
|
|
|
|
2017-07-01 07:47:41 +00:00
|
|
|
use toml::{Value as Toml};
|
2017-08-07 11:38:13 +00:00
|
|
|
use chrono::Utc;
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
use globset::{Glob, GlobSet, GlobSetBuilder};
|
2016-12-06 05:51:33 +00:00
|
|
|
|
2017-02-23 08:34:57 +00:00
|
|
|
use errors::{Result, ResultExt};
|
2017-10-30 20:55:14 +00:00
|
|
|
use highlighting::THEME_SET;
|
2016-12-06 05:51:33 +00:00
|
|
|
|
2017-03-07 12:34:31 +00:00
|
|
|
|
2017-08-24 23:38:03 +00:00
|
|
|
mod theme;
|
|
|
|
|
|
|
|
use theme::Theme;
|
|
|
|
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
2016-12-06 05:51:33 +00:00
|
|
|
pub struct Config {
|
2017-07-27 09:24:43 +00:00
|
|
|
/// Base URL of the site, the only required config argument
|
2016-12-06 05:51:33 +00:00
|
|
|
pub base_url: String,
|
2017-03-10 11:39:58 +00:00
|
|
|
|
2017-08-23 10:17:24 +00:00
|
|
|
/// Theme to use
|
|
|
|
pub theme: Option<String>,
|
2017-07-27 09:24:43 +00:00
|
|
|
/// Title of the site. Defaults to None
|
|
|
|
pub title: Option<String>,
|
2017-03-10 08:28:17 +00:00
|
|
|
/// Whether to highlight all code blocks found in markdown files. Defaults to false
|
2017-03-03 08:12:40 +00:00
|
|
|
pub highlight_code: Option<bool>,
|
2017-03-20 08:30:50 +00:00
|
|
|
/// Which themes to use for code highlighting. See Readme for supported themes
|
|
|
|
pub highlight_theme: Option<String>,
|
2017-02-23 08:34:57 +00:00
|
|
|
/// Description of the site
|
|
|
|
pub description: Option<String>,
|
|
|
|
/// The language used in the site. Defaults to "en"
|
2018-01-12 23:10:19 +00:00
|
|
|
pub default_language: Option<String>,
|
2017-05-20 14:45:31 +00:00
|
|
|
/// Whether to generate RSS. Defaults to false
|
2017-03-12 03:59:28 +00:00
|
|
|
pub generate_rss: Option<bool>,
|
2017-05-20 14:45:31 +00:00
|
|
|
/// The number of articles to include in the RSS feed. Defaults to unlimited
|
|
|
|
pub rss_limit: Option<usize>,
|
2017-03-20 09:36:24 +00:00
|
|
|
/// Whether to generate tags and individual tag pages if some pages have them. Defaults to true
|
|
|
|
pub generate_tags_pages: Option<bool>,
|
|
|
|
/// Whether to generate categories and individual tag categories if some pages have them. Defaults to true
|
|
|
|
pub generate_categories_pages: Option<bool>,
|
2017-07-06 13:19:15 +00:00
|
|
|
/// Whether to compile the `sass` directory and output the css files into the static folder
|
|
|
|
pub compile_sass: Option<bool>,
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
/// A list of file glob patterns to ignore when processing the content folder. Defaults to none.
|
|
|
|
/// Had to remove the PartialEq derive because GlobSet does not implement it. No impact
|
|
|
|
/// because it's unused anyway (who wants to sort Configs?).
|
|
|
|
pub ignored_content: Option<Vec<String>>,
|
|
|
|
#[serde(skip_serializing, skip_deserializing)]
|
|
|
|
pub ignored_content_globber: Option<GlobSet>,
|
2017-03-12 03:59:28 +00:00
|
|
|
|
2018-01-12 23:10:19 +00:00
|
|
|
/// Languages list and translated strings
|
|
|
|
pub translations: Option<HashMap<String, Toml>>,
|
|
|
|
|
2017-02-23 08:34:57 +00:00
|
|
|
/// All user params set in [extra] in the config
|
|
|
|
pub extra: Option<HashMap<String, Toml>>,
|
2017-08-07 11:38:13 +00:00
|
|
|
|
|
|
|
/// Set automatically when instantiating the config. Used for cachebusting
|
|
|
|
pub build_timestamp: Option<i64>,
|
2016-12-06 06:55:17 +00:00
|
|
|
}
|
|
|
|
|
2017-03-20 09:36:24 +00:00
|
|
|
macro_rules! set_default {
|
|
|
|
($key: expr, $default: expr) => {
|
|
|
|
if $key.is_none() {
|
|
|
|
$key = Some($default);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-06 06:55:17 +00:00
|
|
|
impl Config {
|
2017-02-23 08:34:57 +00:00
|
|
|
/// Parses a string containing TOML to our Config struct
|
|
|
|
/// Any extra parameter will end up in the extra field
|
|
|
|
pub fn parse(content: &str) -> Result<Config> {
|
|
|
|
let mut config: Config = match toml::from_str(content) {
|
|
|
|
Ok(c) => c,
|
|
|
|
Err(e) => bail!(e)
|
|
|
|
};
|
2017-03-03 08:12:40 +00:00
|
|
|
|
2018-01-12 23:10:19 +00:00
|
|
|
set_default!(config.default_language, "en".to_string());
|
2017-03-20 09:36:24 +00:00
|
|
|
set_default!(config.highlight_code, false);
|
2017-05-12 13:32:35 +00:00
|
|
|
set_default!(config.generate_rss, false);
|
2017-07-05 03:18:37 +00:00
|
|
|
set_default!(config.rss_limit, 20);
|
2017-05-12 13:32:35 +00:00
|
|
|
set_default!(config.generate_tags_pages, false);
|
|
|
|
set_default!(config.generate_categories_pages, false);
|
2017-07-06 13:19:15 +00:00
|
|
|
set_default!(config.compile_sass, false);
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
set_default!(config.ignored_content, Vec::new());
|
2018-01-12 23:10:19 +00:00
|
|
|
set_default!(config.translations, HashMap::new());
|
2017-08-24 23:38:03 +00:00
|
|
|
set_default!(config.extra, HashMap::new());
|
2017-03-03 08:12:40 +00:00
|
|
|
|
2017-03-20 08:30:50 +00:00
|
|
|
match config.highlight_theme {
|
|
|
|
Some(ref t) => {
|
2017-05-17 12:53:26 +00:00
|
|
|
if !THEME_SET.themes.contains_key(t) {
|
2017-03-20 08:30:50 +00:00
|
|
|
bail!("Theme {} not available", t)
|
|
|
|
}
|
2017-07-01 07:47:41 +00:00
|
|
|
}
|
2017-03-20 08:30:50 +00:00
|
|
|
None => config.highlight_theme = Some("base16-ocean-dark".to_string())
|
|
|
|
};
|
|
|
|
|
2017-08-07 11:38:13 +00:00
|
|
|
config.build_timestamp = Some(Utc::now().timestamp());
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
|
|
|
|
// Convert the file glob strings into a compiled glob set matcher. We want to do this once,
|
|
|
|
// at program initialization, rather than for every page, for example. We arrange for the
|
|
|
|
// globset matcher to always exist (even though it has to be an inside an Option at the
|
|
|
|
// moment because of the TOML serializer); if the glob set is empty the `is_match` function
|
|
|
|
// of the globber always returns false.
|
|
|
|
let mut glob_set_builder = GlobSetBuilder::new();
|
|
|
|
|
|
|
|
if let Some(ref v) = config.ignored_content {
|
2018-02-25 21:28:04 +00:00
|
|
|
for pat in v {
|
|
|
|
let glob = match Glob::new(pat) {
|
|
|
|
Ok(g) => g,
|
|
|
|
Err(e) => bail!("Invalid ignored_content glob pattern: {}, error = {}", pat, e)
|
|
|
|
};
|
|
|
|
glob_set_builder.add(glob);
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
config.ignored_content_globber = Some(glob_set_builder.build().expect("Bad ignored_content in config file."));
|
|
|
|
|
2017-02-23 08:34:57 +00:00
|
|
|
Ok(config)
|
2016-12-06 06:55:17 +00:00
|
|
|
}
|
|
|
|
|
2017-02-23 08:34:57 +00:00
|
|
|
/// Parses a config file from the given path
|
2016-12-06 06:55:17 +00:00
|
|
|
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config> {
|
|
|
|
let mut content = String::new();
|
2016-12-11 06:05:03 +00:00
|
|
|
File::open(path)
|
2017-02-23 08:34:57 +00:00
|
|
|
.chain_err(|| "No `config.toml` file found. Are you in the right directory?")?
|
2016-12-11 06:05:03 +00:00
|
|
|
.read_to_string(&mut content)?;
|
2016-12-06 05:51:33 +00:00
|
|
|
|
2017-02-23 08:34:57 +00:00
|
|
|
Config::parse(&content)
|
2016-12-06 06:55:17 +00:00
|
|
|
}
|
2017-03-20 03:42:43 +00:00
|
|
|
|
|
|
|
/// Makes a url, taking into account that the base url might have a trailing slash
|
|
|
|
pub fn make_permalink(&self, path: &str) -> String {
|
2017-10-24 18:11:39 +00:00
|
|
|
let trailing_bit = if path.ends_with('/') || path.is_empty() { "" } else { "/" };
|
2017-06-10 17:52:39 +00:00
|
|
|
|
|
|
|
// Index section with a base url that has a trailing slash
|
|
|
|
if self.base_url.ends_with('/') && path == "/" {
|
|
|
|
self.base_url.clone()
|
|
|
|
} else if path == "/" {
|
|
|
|
// index section with a base url that doesn't have a trailing slash
|
|
|
|
format!("{}/", self.base_url)
|
|
|
|
} else if self.base_url.ends_with('/') && path.starts_with('/') {
|
|
|
|
format!("{}{}{}", self.base_url, &path[1..], trailing_bit)
|
2017-05-15 07:56:16 +00:00
|
|
|
} else if self.base_url.ends_with('/') {
|
2017-06-10 17:52:39 +00:00
|
|
|
format!("{}{}{}", self.base_url, path, trailing_bit)
|
2017-11-16 17:08:06 +00:00
|
|
|
} else if path.starts_with('/') {
|
|
|
|
format!("{}{}{}", self.base_url, path, trailing_bit)
|
2017-03-20 03:42:43 +00:00
|
|
|
} else {
|
2017-06-10 17:52:39 +00:00
|
|
|
format!("{}/{}{}", self.base_url, path, trailing_bit)
|
2017-03-20 03:42:43 +00:00
|
|
|
}
|
|
|
|
}
|
2017-08-24 23:38:03 +00:00
|
|
|
|
|
|
|
/// Merges the extra data from the theme with the config extra data
|
|
|
|
fn add_theme_extra(&mut self, theme: &Theme) -> Result<()> {
|
|
|
|
if let Some(ref mut config_extra) = self.extra {
|
|
|
|
// 3 pass merging
|
|
|
|
// 1. save config to preserve user
|
|
|
|
let original = config_extra.clone();
|
|
|
|
// 2. inject theme extra values
|
|
|
|
for (key, val) in &theme.extra {
|
2017-08-31 09:01:26 +00:00
|
|
|
config_extra.entry(key.to_string()).or_insert_with(|| val.clone());
|
2017-08-24 23:38:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 3. overwrite with original config
|
|
|
|
for (key, val) in &original {
|
2017-08-31 09:01:26 +00:00
|
|
|
config_extra.entry(key.to_string()).or_insert_with(|| val.clone());
|
2017-08-24 23:38:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse the theme.toml file and merges the extra data from the theme
|
|
|
|
/// with the config extra data
|
|
|
|
pub fn merge_with_theme(&mut self, path: &PathBuf) -> Result<()> {
|
|
|
|
let theme = Theme::from_file(path)?;
|
|
|
|
self.add_theme_extra(&theme)
|
|
|
|
}
|
2016-12-06 06:55:17 +00:00
|
|
|
}
|
|
|
|
|
2017-05-15 07:56:16 +00:00
|
|
|
/// Exists only for testing purposes
|
|
|
|
#[doc(hidden)]
|
2017-03-06 14:45:57 +00:00
|
|
|
impl Default for Config {
|
|
|
|
fn default() -> Config {
|
|
|
|
Config {
|
2017-07-27 09:24:43 +00:00
|
|
|
title: Some("".to_string()),
|
2017-08-23 10:17:24 +00:00
|
|
|
theme: None,
|
2017-03-06 14:45:57 +00:00
|
|
|
base_url: "http://a-website.com/".to_string(),
|
|
|
|
highlight_code: Some(true),
|
2017-03-20 08:30:50 +00:00
|
|
|
highlight_theme: Some("base16-ocean-dark".to_string()),
|
2017-03-06 14:45:57 +00:00
|
|
|
description: None,
|
2018-01-12 23:10:19 +00:00
|
|
|
default_language: Some("en".to_string()),
|
2017-03-12 03:59:28 +00:00
|
|
|
generate_rss: Some(false),
|
2017-08-31 09:01:26 +00:00
|
|
|
rss_limit: Some(10_000),
|
2017-03-20 09:36:24 +00:00
|
|
|
generate_tags_pages: Some(true),
|
|
|
|
generate_categories_pages: Some(true),
|
2017-07-06 13:19:15 +00:00
|
|
|
compile_sass: Some(false),
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
ignored_content: Some(Vec::new()),
|
|
|
|
ignored_content_globber: Some(GlobSetBuilder::new().build().unwrap()),
|
2018-01-12 23:10:19 +00:00
|
|
|
translations: None,
|
2017-03-06 14:45:57 +00:00
|
|
|
extra: None,
|
2017-08-07 11:38:13 +00:00
|
|
|
build_timestamp: Some(1),
|
2017-03-06 14:45:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-11 06:05:03 +00:00
|
|
|
|
2017-03-03 08:12:40 +00:00
|
|
|
/// Get and parse the config.
|
|
|
|
/// If it doesn't succeed, exit
|
2017-03-25 04:18:15 +00:00
|
|
|
pub fn get_config(path: &Path, filename: &str) -> Config {
|
|
|
|
match Config::from_file(path.join(filename)) {
|
2017-03-03 08:12:40 +00:00
|
|
|
Ok(c) => c,
|
|
|
|
Err(e) => {
|
2017-03-25 04:18:15 +00:00
|
|
|
println!("Failed to load {}", filename);
|
2017-03-03 08:12:40 +00:00
|
|
|
println!("Error: {}", e);
|
|
|
|
::std::process::exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-12-06 06:55:17 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2017-08-24 23:38:03 +00:00
|
|
|
use super::{Config, Theme};
|
2016-12-06 06:55:17 +00:00
|
|
|
|
|
|
|
#[test]
|
2017-05-14 05:14:58 +00:00
|
|
|
fn can_import_valid_config() {
|
2016-12-06 06:55:17 +00:00
|
|
|
let config = r#"
|
|
|
|
title = "My site"
|
|
|
|
base_url = "https://replace-this-with-your-url.com"
|
|
|
|
"#;
|
|
|
|
|
2017-02-23 08:34:57 +00:00
|
|
|
let config = Config::parse(config).unwrap();
|
2017-07-27 09:24:43 +00:00
|
|
|
assert_eq!(config.title.unwrap(), "My site".to_string());
|
2016-12-06 06:55:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2017-05-14 05:14:58 +00:00
|
|
|
fn errors_when_invalid_type() {
|
2016-12-06 06:55:17 +00:00
|
|
|
let config = r#"
|
|
|
|
title = 1
|
|
|
|
base_url = "https://replace-this-with-your-url.com"
|
|
|
|
"#;
|
|
|
|
|
2017-02-23 08:34:57 +00:00
|
|
|
let config = Config::parse(config);
|
|
|
|
assert!(config.is_err());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2017-05-14 05:14:58 +00:00
|
|
|
fn errors_when_missing_required_field() {
|
2017-05-12 13:32:35 +00:00
|
|
|
// base_url is required
|
2017-02-23 08:34:57 +00:00
|
|
|
let config = r#"
|
|
|
|
title = ""
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let config = Config::parse(config);
|
2016-12-06 06:55:17 +00:00
|
|
|
assert!(config.is_err());
|
|
|
|
}
|
2017-02-23 08:34:57 +00:00
|
|
|
|
|
|
|
#[test]
|
2017-05-14 05:14:58 +00:00
|
|
|
fn can_add_extra_values() {
|
2017-02-23 08:34:57 +00:00
|
|
|
let config = r#"
|
|
|
|
title = "My site"
|
|
|
|
base_url = "https://replace-this-with-your-url.com"
|
|
|
|
|
|
|
|
[extra]
|
|
|
|
hello = "world"
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let config = Config::parse(config);
|
|
|
|
assert!(config.is_ok());
|
|
|
|
assert_eq!(config.unwrap().extra.unwrap().get("hello").unwrap().as_str().unwrap(), "world");
|
|
|
|
}
|
|
|
|
|
2017-10-24 18:11:39 +00:00
|
|
|
#[test]
|
|
|
|
fn can_make_url_index_page_with_non_trailing_slash_url() {
|
|
|
|
let mut config = Config::default();
|
|
|
|
config.base_url = "http://vincent.is".to_string();
|
|
|
|
assert_eq!(config.make_permalink(""), "http://vincent.is/");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_make_url_index_page_with_railing_slash_url() {
|
|
|
|
let mut config = Config::default();
|
|
|
|
config.base_url = "http://vincent.is/".to_string();
|
|
|
|
assert_eq!(config.make_permalink(""), "http://vincent.is/");
|
|
|
|
}
|
|
|
|
|
2017-05-15 07:56:16 +00:00
|
|
|
#[test]
|
|
|
|
fn can_make_url_with_non_trailing_slash_base_url() {
|
|
|
|
let mut config = Config::default();
|
|
|
|
config.base_url = "http://vincent.is".to_string();
|
2017-06-10 17:52:39 +00:00
|
|
|
assert_eq!(config.make_permalink("hello"), "http://vincent.is/hello/");
|
2017-05-15 07:56:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_make_url_with_trailing_slash_path() {
|
|
|
|
let mut config = Config::default();
|
|
|
|
config.base_url = "http://vincent.is/".to_string();
|
2017-06-10 17:52:39 +00:00
|
|
|
assert_eq!(config.make_permalink("/hello"), "http://vincent.is/hello/");
|
2017-05-15 07:56:16 +00:00
|
|
|
}
|
2017-08-24 23:38:03 +00:00
|
|
|
|
2017-11-16 17:08:06 +00:00
|
|
|
#[test]
|
|
|
|
fn can_make_url_with_localhost() {
|
|
|
|
let mut config = Config::default();
|
|
|
|
config.base_url = "http://127.0.0.1:1111".to_string();
|
|
|
|
assert_eq!(config.make_permalink("/tags/rust"), "http://127.0.0.1:1111/tags/rust/");
|
|
|
|
}
|
|
|
|
|
2017-08-24 23:38:03 +00:00
|
|
|
#[test]
|
|
|
|
fn can_merge_with_theme_data_and_preserve_config_value() {
|
|
|
|
let config_str = r#"
|
|
|
|
title = "My site"
|
|
|
|
base_url = "https://replace-this-with-your-url.com"
|
|
|
|
|
|
|
|
[extra]
|
|
|
|
hello = "world"
|
|
|
|
"#;
|
|
|
|
let mut config = Config::parse(config_str).unwrap();
|
|
|
|
let theme_str = r#"
|
|
|
|
[extra]
|
|
|
|
hello = "foo"
|
|
|
|
a_value = 10
|
|
|
|
"#;
|
|
|
|
let theme = Theme::parse(theme_str).unwrap();
|
|
|
|
assert!(config.add_theme_extra(&theme).is_ok());
|
|
|
|
let extra = config.extra.unwrap();
|
|
|
|
assert_eq!(extra["hello"].as_str().unwrap(), "world".to_string());
|
|
|
|
assert_eq!(extra["a_value"].as_integer().unwrap(), 10);
|
|
|
|
}
|
2018-01-12 23:10:19 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn can_use_language_configuration() {
|
|
|
|
let config = r#"
|
|
|
|
base_url = "https://remplace-par-ton-url.fr"
|
|
|
|
default_language = "fr"
|
|
|
|
|
|
|
|
[translations]
|
|
|
|
[translations.fr]
|
|
|
|
title = "Un titre"
|
|
|
|
|
|
|
|
[translations.en]
|
|
|
|
title = "A title"
|
|
|
|
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let config = Config::parse(config);
|
|
|
|
assert!(config.is_ok());
|
|
|
|
let translations = config.unwrap().translations.unwrap();
|
|
|
|
assert_eq!(translations["fr"]["title"].as_str().unwrap(), "Un titre");
|
|
|
|
assert_eq!(translations["en"]["title"].as_str().unwrap(), "A title");
|
|
|
|
}
|
|
|
|
|
Filter ignored content in page.rs.
* Add ignored_content to the Config structure.
* Use the GlobSet crate to parse the glob patterns into a matcher, which
is created once at program initialization. If there are no patterns in
ignored_content, an empty globber is created, which excludes no files.
This is consistent with the existing behaviour of Gutenberg, before
this feature was added.
* Bail if there are any errors in the glob patterns.
* Add a call to the globber in page.rs to actually do the filtering.
* Update documentation.
A note on the Config structure
------------------------------
* I had to remove the PartialEq derive from the Config structure as it
does not work for the GlobSet type. No harm is done, Config does not
need to be PartialEq anyway, since there is no need to sort Configs.
* The implementation follows the pattern of the existing config settings
in that it uses an Option<...>. This would appear unnecessary, in that
an empty vec could be used as the default, but it appears to be needed
by the TOML parsing. A better approach would be to use a separate
SerializableConfig and map to/from a Config struct. This would also
allow the elimination of most, if not all, of the other Options in
the Config structure, but that ought to be another PR.
2018-02-25 11:42:31 +00:00
|
|
|
#[test]
|
|
|
|
fn missing_ignored_content_results_in_empty_vector_and_empty_globber() {
|
|
|
|
let config_str = r#"
|
|
|
|
title = "My site"
|
|
|
|
base_url = "example.com"
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let config = Config::parse(config_str).unwrap();
|
|
|
|
let v = config.ignored_content.unwrap();
|
|
|
|
assert_eq!(v.len(), 0);
|
|
|
|
assert!(config.ignored_content_globber.unwrap().is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn empty_ignored_content_results_in_empty_vector_and_empty_globber() {
|
|
|
|
let config_str = r#"
|
|
|
|
title = "My site"
|
|
|
|
base_url = "example.com"
|
|
|
|
ignored_content = []
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let config = Config::parse(config_str).unwrap();
|
|
|
|
assert_eq!(config.ignored_content.unwrap().len(), 0);
|
|
|
|
assert!(config.ignored_content_globber.unwrap().is_empty());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn non_empty_ignored_content_results_in_vector_of_patterns_and_configured_globber() {
|
|
|
|
let config_str = r#"
|
|
|
|
title = "My site"
|
|
|
|
base_url = "example.com"
|
|
|
|
ignored_content = ["*.{graphml,iso}", "*.py?"]
|
|
|
|
"#;
|
|
|
|
|
|
|
|
let config = Config::parse(config_str).unwrap();
|
|
|
|
let v = config.ignored_content.unwrap();
|
|
|
|
assert_eq!(v, vec!["*.{graphml,iso}", "*.py?"]);
|
|
|
|
|
|
|
|
let g = config.ignored_content_globber.unwrap();
|
|
|
|
assert_eq!(g.len(), 2);
|
|
|
|
assert!(g.is_match("foo.graphml"));
|
|
|
|
assert!(g.is_match("foo.iso"));
|
|
|
|
assert!(!g.is_match("foo.png"));
|
|
|
|
assert!(g.is_match("foo.py2"));
|
|
|
|
assert!(g.is_match("foo.py3"));
|
|
|
|
assert!(!g.is_match("foo.py"));
|
|
|
|
}
|
2016-12-06 06:55:17 +00:00
|
|
|
}
|