@@ -1,6 +1,6 @@ | |||
target | |||
.idea/ | |||
components/site/test_site/public | |||
test_site/public | |||
docs/public | |||
small-blog | |||
@@ -1153,7 +1153,6 @@ dependencies = [ | |||
"templates 0.1.0", | |||
"tera 0.11.5 (registry+https://github.com/rust-lang/crates.io-index)", | |||
"utils 0.1.0", | |||
"walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
@@ -1465,6 +1464,7 @@ dependencies = [ | |||
"errors 0.1.0", | |||
"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)", | |||
"walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", | |||
] | |||
[[package]] | |||
@@ -94,6 +94,10 @@ pub struct PageFrontMatter { | |||
/// Specify a template different from `page.html` to use for that page | |||
#[serde(skip_serializing)] | |||
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 | |||
#[serde(default)] | |||
pub extra: Map<String, Value>, | |||
@@ -174,6 +178,7 @@ impl Default for PageFrontMatter { | |||
order: None, | |||
weight: None, | |||
aliases: None, | |||
in_search_index: true, | |||
template: None, | |||
extra: Map::new(), | |||
} | |||
@@ -48,7 +48,7 @@ pub struct SectionFrontMatter { | |||
#[serde(skip_serializing)] | |||
pub redirect_to: Option<String>, | |||
/// 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)] | |||
pub in_search_index: bool, | |||
/// Any extra parameter present in the front matter | |||
@@ -6,7 +6,6 @@ authors = ["Vincent Prouillet <prouillet.vincent@gmail.com>"] | |||
[dependencies] | |||
tera = "0.11" | |||
glob = "0.2" | |||
walkdir = "2" | |||
rayon = "1" | |||
serde = "1" | |||
serde_derive = "1" | |||
@@ -1,7 +1,6 @@ | |||
extern crate tera; | |||
extern crate rayon; | |||
extern crate glob; | |||
extern crate walkdir; | |||
extern crate serde; | |||
#[macro_use] | |||
extern crate serde_derive; | |||
@@ -21,7 +20,7 @@ extern crate content; | |||
extern crate tempdir; | |||
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::path::{Path, PathBuf}; | |||
@@ -32,7 +31,7 @@ use sass_rs::{Options as SassOptions, OutputStyle, compile_file}; | |||
use errors::{Result, ResultExt}; | |||
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 content::{Page, Section, populate_previous_and_next_pages, sort_pages}; | |||
use templates::{GUTENBERG_TERA, global_fns, render_redirect_template}; | |||
@@ -67,7 +66,7 @@ pub struct Site { | |||
pub sections: HashMap<PathBuf, Section>, | |||
pub tera: Tera, | |||
live_reload: bool, | |||
output_path: PathBuf, | |||
pub output_path: PathBuf, | |||
pub static_path: PathBuf, | |||
pub tags: Option<Taxonomy>, | |||
pub categories: Option<Taxonomy>, | |||
@@ -125,6 +124,11 @@ impl 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 | |||
pub fn enable_live_reload(&mut self) { | |||
self.live_reload = true; | |||
@@ -198,7 +202,17 @@ impl Site { | |||
// Insert a default index section if necessary so we don't need to create | |||
// 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) { | |||
let mut index_section = Section::default(); | |||
index_section.permalink = self.config.make_permalink(""); | |||
@@ -409,45 +423,18 @@ impl Site { | |||
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 | |||
pub fn copy_static_directories(&self) -> Result<()> { | |||
// The user files will overwrite the theme files | |||
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 | |||
if self.static_path.exists() { | |||
self.copy_static_directory(&self.static_path)?; | |||
copy_directory(&self.static_path, &self.output_path)?; | |||
} | |||
Ok(()) | |||
@@ -445,3 +445,10 @@ fn can_build_rss_feed() { | |||
// Next is posts/python.md | |||
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] | |||
errors = { path = "../errors" } | |||
tera = "0.11" | |||
walkdir = "2" | |||
[dev-dependencies] | |||
@@ -1,9 +1,12 @@ | |||
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 walkdir::WalkDir; | |||
use errors::{Result, ResultExt}; | |||
/// Create a file with the content given | |||
pub fn create_file(path: &Path, content: &str) -> Result<()> { | |||
let mut file = File::create(&path)?; | |||
@@ -60,6 +63,36 @@ pub fn find_related_assets(path: &Path) -> Vec<PathBuf> { | |||
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)] | |||
mod tests { | |||
use std::fs::File; | |||
@@ -4,6 +4,7 @@ extern crate errors; | |||
#[cfg(test)] | |||
extern crate tempdir; | |||
extern crate tera; | |||
extern crate walkdir; | |||
pub mod fs; | |||
pub mod site; | |||
@@ -38,6 +38,7 @@ use ctrlc; | |||
use site::Site; | |||
use errors::{Result, ResultExt}; | |||
use utils::fs::copy_file; | |||
use console; | |||
use rebuild; | |||
@@ -207,7 +208,7 @@ pub fn serve(interface: &str, port: &str, output_dir: &str, base_url: &str, conf | |||
(ChangeKind::StaticFiles, p) => { | |||
if path.is_file() { | |||
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) => { | |||