@@ -6,6 +6,6 @@ use gutenberg::Site; | |||||
pub fn build() -> Result<()> { | pub fn build() -> Result<()> { | ||||
let mut site = Site::new(env::current_dir().unwrap())?; | let mut site = Site::new(env::current_dir().unwrap())?; | ||||
site.parse()?; | |||||
site.load()?; | |||||
site.build() | site.build() | ||||
} | } |
@@ -70,7 +70,7 @@ pub fn serve(interface: &str, port: &str) -> Result<()> { | |||||
format!("http://{}", address) | format!("http://{}", address) | ||||
}; | }; | ||||
site.parse()?; | |||||
site.load()?; | |||||
site.enable_live_reload(); | site.enable_live_reload(); | ||||
site.build()?; | site.build()?; | ||||
report_elapsed_time(start); | report_elapsed_time(start); | ||||
@@ -129,7 +129,7 @@ pub fn serve(interface: &str, port: &str) -> Result<()> { | |||||
(ChangeKind::Content, _) => { | (ChangeKind::Content, _) => { | ||||
println!("-> Content changed {}", path.display()); | println!("-> Content changed {}", path.display()); | ||||
// Force refresh | // Force refresh | ||||
rebuild_done_handling(&broadcaster, site.rebuild_after_content_change(), "/x.js"); | |||||
rebuild_done_handling(&broadcaster, site.rebuild_after_content_change(&path), "/x.js"); | |||||
}, | }, | ||||
(ChangeKind::Templates, _) => { | (ChangeKind::Templates, _) => { | ||||
println!("-> Template changed {}", path.display()); | println!("-> Template changed {}", path.display()); | ||||
@@ -101,39 +101,60 @@ impl Site { | |||||
self.output_path = path.as_ref().to_path_buf(); | self.output_path = path.as_ref().to_path_buf(); | ||||
} | } | ||||
/// Reads all .md files in the `content` directory and create pages | |||||
/// Reads all .md files in the `content` directory and create pages/sections | |||||
/// out of them | /// out of them | ||||
pub fn parse(&mut self) -> Result<()> { | |||||
pub fn load(&mut self) -> Result<()> { | |||||
let path = self.base_path.to_string_lossy().replace("\\", "/"); | let path = self.base_path.to_string_lossy().replace("\\", "/"); | ||||
let content_glob = format!("{}/{}", path, "content/**/*.md"); | let content_glob = format!("{}/{}", path, "content/**/*.md"); | ||||
// parent_dir -> Section | |||||
let mut sections = BTreeMap::new(); | |||||
// Glob is giving us the result order so _index will show up first | |||||
// for each directory | |||||
// TODO: make that parallel, that's the main bottleneck | |||||
// `add_section` and `add_page` can't be used in the parallel version afaik | |||||
for entry in glob(&content_glob).unwrap().filter_map(|e| e.ok()) { | for entry in glob(&content_glob).unwrap().filter_map(|e| e.ok()) { | ||||
let path = entry.as_path(); | let path = entry.as_path(); | ||||
if path.file_name().unwrap() == "_index.md" { | if path.file_name().unwrap() == "_index.md" { | ||||
let section = Section::from_file(&path, &self.config)?; | |||||
sections.insert(section.parent_path.clone(), section); | |||||
self.add_section(&path)?; | |||||
} else { | } else { | ||||
let page = Page::from_file(&path, &self.config)?; | |||||
if sections.contains_key(&page.parent_path) { | |||||
sections.get_mut(&page.parent_path).unwrap().pages.push(page.clone()); | |||||
} | |||||
self.pages.insert(page.file_path.clone(), page); | |||||
self.add_page(&path)?; | |||||
} | |||||
} | |||||
self.populate_sections(); | |||||
self.populate_tags_and_categories(); | |||||
Ok(()) | |||||
} | |||||
/// Simple wrapper fn to avoid repeating that code in several places | |||||
fn add_page(&mut self, path: &Path) -> Result<()> { | |||||
let page = Page::from_file(&path, &self.config)?; | |||||
self.pages.insert(page.file_path.clone(), page); | |||||
Ok(()) | |||||
} | |||||
/// Simple wrapper fn to avoid repeating that code in several places | |||||
fn add_section(&mut self, path: &Path) -> Result<()> { | |||||
let section = Section::from_file(path, &self.config)?; | |||||
self.sections.insert(section.parent_path.clone(), section); | |||||
Ok(()) | |||||
} | |||||
/// Find out the direct subsections of each subsection if there are some | |||||
/// as well as the pages for each section | |||||
fn populate_sections(&mut self) { | |||||
for page in self.pages.values() { | |||||
if self.sections.contains_key(&page.parent_path) { | |||||
self.sections.get_mut(&page.parent_path).unwrap().pages.push(page.clone()); | |||||
} | } | ||||
} | } | ||||
// Find out the direct subsections of each subsection if there are some | |||||
let mut grandparent_paths = HashMap::new(); | let mut grandparent_paths = HashMap::new(); | ||||
for section in sections.values() { | |||||
for section in self.sections.values() { | |||||
let grand_parent = section.parent_path.parent().unwrap().to_path_buf(); | let grand_parent = section.parent_path.parent().unwrap().to_path_buf(); | ||||
grandparent_paths.entry(grand_parent).or_insert_with(|| vec![]).push(section.clone()); | grandparent_paths.entry(grand_parent).or_insert_with(|| vec![]).push(section.clone()); | ||||
} | } | ||||
for (parent_path, section) in &mut sections { | |||||
for (parent_path, section) in &mut self.sections { | |||||
section.pages.sort_by(|a, b| a.partial_cmp(b).unwrap()); | section.pages.sort_by(|a, b| a.partial_cmp(b).unwrap()); | ||||
match grandparent_paths.get(parent_path) { | match grandparent_paths.get(parent_path) { | ||||
@@ -141,15 +162,10 @@ impl Site { | |||||
None => continue, | None => continue, | ||||
}; | }; | ||||
} | } | ||||
self.sections = sections; | |||||
self.parse_tags_and_categories(); | |||||
Ok(()) | |||||
} | } | ||||
/// Separated from `parse` for easier testing | /// Separated from `parse` for easier testing | ||||
pub fn parse_tags_and_categories(&mut self) { | |||||
pub fn populate_tags_and_categories(&mut self) { | |||||
for page in self.pages.values() { | for page in self.pages.values() { | ||||
if let Some(ref category) = page.meta.category { | if let Some(ref category) = page.meta.category { | ||||
self.categories | self.categories | ||||
@@ -221,8 +237,26 @@ impl Site { | |||||
Ok(()) | Ok(()) | ||||
} | } | ||||
pub fn rebuild_after_content_change(&mut self) -> Result<()> { | |||||
self.parse()?; | |||||
pub fn rebuild_after_content_change(&mut self, path: &Path) -> Result<()> { | |||||
if path.exists() { | |||||
// file exists, either a new one or updating content | |||||
if self.pages.contains_key(path) { | |||||
if path.ends_with("_index.md") { | |||||
self.add_section(path)?; | |||||
} else { | |||||
// probably just an update so just re-parse that page | |||||
self.add_page(path)?; | |||||
} | |||||
} else { | |||||
// new file? | |||||
self.add_page(path)?; | |||||
} | |||||
} else { | |||||
// File doesn't exist -> a deletion so we remove it from | |||||
self.pages.remove(path); | |||||
} | |||||
self.populate_sections(); | |||||
self.populate_tags_and_categories(); | |||||
self.build() | self.build() | ||||
} | } | ||||
@@ -17,7 +17,7 @@ fn test_can_parse_site() { | |||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | let mut site = Site::new(&path).unwrap(); | ||||
site.parse().unwrap(); | |||||
site.load().unwrap(); | |||||
// Correct number of pages (sections are pages too) | // Correct number of pages (sections are pages too) | ||||
assert_eq!(site.pages.len(), 10); | assert_eq!(site.pages.len(), 10); | ||||
@@ -89,7 +89,7 @@ fn test_can_build_site_without_live_reload() { | |||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | let mut site = Site::new(&path).unwrap(); | ||||
site.parse().unwrap(); | |||||
site.load().unwrap(); | |||||
let tmp_dir = TempDir::new("example").expect("create temp dir"); | let tmp_dir = TempDir::new("example").expect("create temp dir"); | ||||
let public = &tmp_dir.path().join("public"); | let public = &tmp_dir.path().join("public"); | ||||
site.set_output_path(&public); | site.set_output_path(&public); | ||||
@@ -130,7 +130,7 @@ fn test_can_build_site_with_live_reload() { | |||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | let mut site = Site::new(&path).unwrap(); | ||||
site.parse().unwrap(); | |||||
site.load().unwrap(); | |||||
let tmp_dir = TempDir::new("example").expect("create temp dir"); | let tmp_dir = TempDir::new("example").expect("create temp dir"); | ||||
let public = &tmp_dir.path().join("public"); | let public = &tmp_dir.path().join("public"); | ||||
site.set_output_path(&public); | site.set_output_path(&public); | ||||
@@ -168,7 +168,7 @@ fn test_can_build_site_with_categories() { | |||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | let mut site = Site::new(&path).unwrap(); | ||||
site.parse().unwrap(); | |||||
site.load().unwrap(); | |||||
for (i, page) in site.pages.values_mut().enumerate() { | for (i, page) in site.pages.values_mut().enumerate() { | ||||
page.meta.category = if i % 2 == 0 { | page.meta.category = if i % 2 == 0 { | ||||
@@ -177,7 +177,7 @@ fn test_can_build_site_with_categories() { | |||||
Some("B".to_string()) | Some("B".to_string()) | ||||
}; | }; | ||||
} | } | ||||
site.parse_tags_and_categories(); | |||||
site.populate_tags_and_categories(); | |||||
let tmp_dir = TempDir::new("example").expect("create temp dir"); | let tmp_dir = TempDir::new("example").expect("create temp dir"); | ||||
let public = &tmp_dir.path().join("public"); | let public = &tmp_dir.path().join("public"); | ||||
site.set_output_path(&public); | site.set_output_path(&public); | ||||
@@ -219,7 +219,7 @@ fn test_can_build_site_with_tags() { | |||||
let mut path = env::current_dir().unwrap().to_path_buf(); | let mut path = env::current_dir().unwrap().to_path_buf(); | ||||
path.push("test_site"); | path.push("test_site"); | ||||
let mut site = Site::new(&path).unwrap(); | let mut site = Site::new(&path).unwrap(); | ||||
site.parse().unwrap(); | |||||
site.load().unwrap(); | |||||
for (i, page) in site.pages.values_mut().enumerate() { | for (i, page) in site.pages.values_mut().enumerate() { | ||||
page.meta.tags = if i % 2 == 0 { | page.meta.tags = if i % 2 == 0 { | ||||
@@ -228,7 +228,7 @@ fn test_can_build_site_with_tags() { | |||||
Some(vec!["tag with space".to_string()]) | Some(vec!["tag with space".to_string()]) | ||||
}; | }; | ||||
} | } | ||||
site.parse_tags_and_categories(); | |||||
site.populate_tags_and_categories(); | |||||
let tmp_dir = TempDir::new("example").expect("create temp dir"); | let tmp_dir = TempDir::new("example").expect("create temp dir"); | ||||
let public = &tmp_dir.path().join("public"); | let public = &tmp_dir.path().join("public"); | ||||