@@ -1,6 +1,6 @@ | |||||
target | target | ||||
.idea/ | .idea/ | ||||
components/site/test_site/public | |||||
test_site/public | |||||
docs/public | docs/public | ||||
small-blog | small-blog | ||||
@@ -1153,7 +1153,6 @@ dependencies = [ | |||||
"templates 0.1.0", | "templates 0.1.0", | ||||
"tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", | "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"utils 0.1.0", | "utils 0.1.0", | ||||
"walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | ] | ||||
[[package]] | [[package]] | ||||
@@ -1465,6 +1464,7 @@ dependencies = [ | |||||
"errors 0.1.0", | "errors 0.1.0", | ||||
"tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", | "tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", | ||||
"walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||||
] | ] | ||||
[[package]] | [[package]] | ||||
@@ -94,6 +94,10 @@ pub struct PageFrontMatter { | |||||
/// Specify a template different from `page.html` to use for that page | /// Specify a template different from `page.html` to use for that page | ||||
#[serde(skip_serializing)] | #[serde(skip_serializing)] | ||||
pub template: Option<String>, | pub template: Option<String>, | ||||
/// Whether the page is included in the search index | |||||
/// Defaults to `true` but is only used if search if explicitly enabled in the config. | |||||
#[serde(default, skip_serializing)] | |||||
pub in_search_index: bool, | |||||
/// Any extra parameter present in the front matter | /// Any extra parameter present in the front matter | ||||
#[serde(default)] | #[serde(default)] | ||||
pub extra: Map<String, Value>, | pub extra: Map<String, Value>, | ||||
@@ -174,6 +178,7 @@ impl Default for PageFrontMatter { | |||||
order: None, | order: None, | ||||
weight: None, | weight: None, | ||||
aliases: None, | aliases: None, | ||||
in_search_index: true, | |||||
template: None, | template: None, | ||||
extra: Map::new(), | extra: Map::new(), | ||||
} | } | ||||
@@ -48,7 +48,7 @@ pub struct SectionFrontMatter { | |||||
#[serde(skip_serializing)] | #[serde(skip_serializing)] | ||||
pub redirect_to: Option<String>, | pub redirect_to: Option<String>, | ||||
/// Whether the section content and its pages/subsections are included in the index. | /// Whether the section content and its pages/subsections are included in the index. | ||||
/// Defaults to `true` but is only used if search if explicitely enabled in the config. | |||||
/// Defaults to `true` but is only used if search if explicitly enabled in the config. | |||||
#[serde(skip_serializing)] | #[serde(skip_serializing)] | ||||
pub in_search_index: bool, | pub in_search_index: bool, | ||||
/// Any extra parameter present in the front matter | /// Any extra parameter present in the front matter | ||||
@@ -6,7 +6,6 @@ authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||||
[dependencies] | [dependencies] | ||||
tera = "0.11" | tera = "0.11" | ||||
glob = "0.2" | glob = "0.2" | ||||
walkdir = "2" | |||||
rayon = "1" | rayon = "1" | ||||
serde = "1" | serde = "1" | ||||
serde_derive = "1" | serde_derive = "1" | ||||
@@ -1,7 +1,6 @@ | |||||
extern crate tera; | extern crate tera; | ||||
extern crate rayon; | extern crate rayon; | ||||
extern crate glob; | extern crate glob; | ||||
extern crate walkdir; | |||||
extern crate serde; | extern crate serde; | ||||
#[macro_use] | #[macro_use] | ||||
extern crate serde_derive; | extern crate serde_derive; | ||||
@@ -21,7 +20,7 @@ extern crate content; | |||||
extern crate tempdir; | extern crate tempdir; | ||||
use std::collections::HashMap; | use std::collections::HashMap; | ||||
use std::fs::{remove_dir_all, copy, create_dir_all}; | |||||
use std::fs::{remove_dir_all, copy}; | |||||
use std::mem; | use std::mem; | ||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||
@@ -32,7 +31,7 @@ use sass_rs::{Options as SassOptions, OutputStyle, compile_file}; | |||||
use errors::{Result, ResultExt}; | use errors::{Result, ResultExt}; | ||||
use config::{Config, get_config}; | use config::{Config, get_config}; | ||||
use utils::fs::{create_file, create_directory, ensure_directory_exists}; | |||||
use utils::fs::{create_file, copy_directory, create_directory, ensure_directory_exists}; | |||||
use utils::templates::{render_template, rewrite_theme_paths}; | use utils::templates::{render_template, rewrite_theme_paths}; | ||||
use content::{Page, Section, populate_previous_and_next_pages, sort_pages}; | use content::{Page, Section, populate_previous_and_next_pages, sort_pages}; | ||||
use templates::{GUTENBERG_TERA, global_fns, render_redirect_template}; | use templates::{GUTENBERG_TERA, global_fns, render_redirect_template}; | ||||
@@ -67,7 +66,7 @@ pub struct Site { | |||||
pub sections: HashMap<PathBuf, Section>, | pub sections: HashMap<PathBuf, Section>, | ||||
pub tera: Tera, | pub tera: Tera, | ||||
live_reload: bool, | live_reload: bool, | ||||
output_path: PathBuf, | |||||
pub output_path: PathBuf, | |||||
pub static_path: PathBuf, | pub static_path: PathBuf, | ||||
pub tags: Option<Taxonomy>, | pub tags: Option<Taxonomy>, | ||||
pub categories: Option<Taxonomy>, | pub categories: Option<Taxonomy>, | ||||
@@ -125,6 +124,11 @@ impl Site { | |||||
Ok(site) | Ok(site) | ||||
} | } | ||||
/// The index section is ALWAYS at that path | |||||
pub fn index_section_path(&self) -> PathBuf { | |||||
self.base_path.join("content").join("_index.md") | |||||
} | |||||
/// What the function name says | /// What the function name says | ||||
pub fn enable_live_reload(&mut self) { | pub fn enable_live_reload(&mut self) { | ||||
self.live_reload = true; | self.live_reload = true; | ||||
@@ -198,7 +202,17 @@ impl Site { | |||||
// Insert a default index section if necessary so we don't need to create | // Insert a default index section if necessary so we don't need to create | ||||
// a _index.md to render the index page | // a _index.md to render the index page | ||||
let index_path = self.base_path.join("content").join("_index.md"); | |||||
let index_path = self.index_section_path(); | |||||
if let Some(ref index_section) = self.sections.get(&index_path) { | |||||
if self.config.build_search_index && index_section.meta.in_search_index { | |||||
bail!( | |||||
"You have enabled search in the config but disabled it in the index section: \ | |||||
either turn off the search in the config or remote `in_search_index = true` from the \ | |||||
section front-matter." | |||||
) | |||||
} | |||||
} | |||||
// Not in else because of borrow checker | |||||
if !self.sections.contains_key(&index_path) { | if !self.sections.contains_key(&index_path) { | ||||
let mut index_section = Section::default(); | let mut index_section = Section::default(); | ||||
index_section.permalink = self.config.make_permalink(""); | index_section.permalink = self.config.make_permalink(""); | ||||
@@ -409,45 +423,18 @@ impl Site { | |||||
html | html | ||||
} | } | ||||
/// Copy the file at the given path into the public folder | |||||
pub fn copy_static_file<P: AsRef<Path>>(&self, path: P, base_path: &PathBuf) -> Result<()> { | |||||
let relative_path = path.as_ref().strip_prefix(base_path).unwrap(); | |||||
let target_path = self.output_path.join(relative_path); | |||||
if let Some(parent_directory) = target_path.parent() { | |||||
create_dir_all(parent_directory)?; | |||||
} | |||||
copy(path.as_ref(), &target_path)?; | |||||
Ok(()) | |||||
} | |||||
/// Copy the content of the given folder into the `public` folder | |||||
fn copy_static_directory(&self, path: &PathBuf) -> Result<()> { | |||||
for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) { | |||||
let relative_path = entry.path().strip_prefix(path).unwrap(); | |||||
let target_path = self.output_path.join(relative_path); | |||||
if entry.path().is_dir() { | |||||
if !target_path.exists() { | |||||
create_directory(&target_path)?; | |||||
} | |||||
} else { | |||||
let entry_fullpath = self.base_path.join(entry.path()); | |||||
self.copy_static_file(entry_fullpath, path)?; | |||||
} | |||||
} | |||||
Ok(()) | |||||
} | |||||
/// Copy the main `static` folder and the theme `static` folder if a theme is used | /// Copy the main `static` folder and the theme `static` folder if a theme is used | ||||
pub fn copy_static_directories(&self) -> Result<()> { | pub fn copy_static_directories(&self) -> Result<()> { | ||||
// The user files will overwrite the theme files | // The user files will overwrite the theme files | ||||
if let Some(ref theme) = self.config.theme { | if let Some(ref theme) = self.config.theme { | ||||
self.copy_static_directory( | |||||
&self.base_path.join("themes").join(theme).join("static") | |||||
copy_directory( | |||||
&self.base_path.join("themes").join(theme).join("static"), | |||||
&self.output_path | |||||
)?; | )?; | ||||
} | } | ||||
// We're fine with missing static folders | // We're fine with missing static folders | ||||
if self.static_path.exists() { | if self.static_path.exists() { | ||||
self.copy_static_directory(&self.static_path)?; | |||||
copy_directory(&self.static_path, &self.output_path)?; | |||||
} | } | ||||
Ok(()) | Ok(()) | ||||
@@ -445,3 +445,10 @@ fn can_build_rss_feed() { | |||||
// Next is posts/python.md | // Next is posts/python.md | ||||
assert!(file_contains!(public, "rss.xml", "Python in posts")); | assert!(file_contains!(public, "rss.xml", "Python in posts")); | ||||
} | } | ||||
#[test] | |||||
fn can_build_search_index() { | |||||
// TODO: generate an index somehow and check for correctness with | |||||
// another one | |||||
} |
@@ -6,6 +6,7 @@ authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||||
[dependencies] | [dependencies] | ||||
errors = { path = "../errors" } | errors = { path = "../errors" } | ||||
tera = "0.11" | tera = "0.11" | ||||
walkdir = "2" | |||||
[dev-dependencies] | [dev-dependencies] | ||||
@@ -1,9 +1,12 @@ | |||||
use std::io::prelude::*; | use std::io::prelude::*; | ||||
use std::fs::{File, create_dir_all, read_dir}; | |||||
use std::fs::{File, create_dir_all, read_dir, copy}; | |||||
use std::path::{Path, PathBuf}; | use std::path::{Path, PathBuf}; | ||||
use walkdir::WalkDir; | |||||
use errors::{Result, ResultExt}; | use errors::{Result, ResultExt}; | ||||
/// Create a file with the content given | /// Create a file with the content given | ||||
pub fn create_file(path: &Path, content: &str) -> Result<()> { | pub fn create_file(path: &Path, content: &str) -> Result<()> { | ||||
let mut file = File::create(&path)?; | let mut file = File::create(&path)?; | ||||
@@ -60,6 +63,36 @@ pub fn find_related_assets(path: &Path) -> Vec<PathBuf> { | |||||
assets | assets | ||||
} | } | ||||
/// Copy a file but takes into account where to start the copy as | |||||
/// there might be folders we need to create on the way | |||||
pub fn copy_file(src: &Path, dest: &PathBuf, base_path: &PathBuf) -> Result<()> { | |||||
let relative_path = src.strip_prefix(base_path).unwrap(); | |||||
let target_path = dest.join(relative_path); | |||||
if let Some(parent_directory) = target_path.parent() { | |||||
create_dir_all(parent_directory)?; | |||||
} | |||||
copy(src, target_path)?; | |||||
Ok(()) | |||||
} | |||||
pub fn copy_directory(src: &PathBuf, dest: &PathBuf) -> Result<()> { | |||||
for entry in WalkDir::new(src).into_iter().filter_map(|e| e.ok()) { | |||||
let relative_path = entry.path().strip_prefix(src).unwrap(); | |||||
let target_path = dest.join(relative_path); | |||||
if entry.path().is_dir() { | |||||
if !target_path.exists() { | |||||
create_directory(&target_path)?; | |||||
} | |||||
} else { | |||||
copy_file(entry.path(), dest, src)?; | |||||
} | |||||
} | |||||
Ok(()) | |||||
} | |||||
#[cfg(test)] | #[cfg(test)] | ||||
mod tests { | mod tests { | ||||
use std::fs::File; | use std::fs::File; | ||||
@@ -4,6 +4,7 @@ extern crate errors; | |||||
#[cfg(test)] | #[cfg(test)] | ||||
extern crate tempdir; | extern crate tempdir; | ||||
extern crate tera; | extern crate tera; | ||||
extern crate walkdir; | |||||
pub mod fs; | pub mod fs; | ||||
pub mod site; | pub mod site; | ||||
@@ -38,6 +38,7 @@ use ctrlc; | |||||
use site::Site; | use site::Site; | ||||
use errors::{Result, ResultExt}; | use errors::{Result, ResultExt}; | ||||
use utils::fs::copy_file; | |||||
use console; | use console; | ||||
use rebuild; | use rebuild; | ||||
@@ -207,7 +208,7 @@ pub fn serve(interface: &str, port: &str, output_dir: &str, base_url: &str, conf | |||||
(ChangeKind::StaticFiles, p) => { | (ChangeKind::StaticFiles, p) => { | ||||
if path.is_file() { | if path.is_file() { | ||||
console::info(&format!("-> Static file changes detected {}", path.display())); | console::info(&format!("-> Static file changes detected {}", path.display())); | ||||
rebuild_done_handling(&broadcaster, site.copy_static_file(&path, &site.static_path), &p); | |||||
rebuild_done_handling(&broadcaster, copy_file(&path, &site.output_path, &site.static_path), &p); | |||||
} | } | ||||
}, | }, | ||||
(ChangeKind::Sass, p) => { | (ChangeKind::Sass, p) => { | ||||