From dde3531fd9beb4ea2e16ef904fb5808ebdab5321 Mon Sep 17 00:00:00 2001 From: Sam Vente Date: Thu, 3 Mar 2022 12:35:29 +0100 Subject: [PATCH] Make sure that zola serve/build can start from anywhere inside dir tree (#1761) * Make sure that zola serve/build can start from anywhere inside dir tree * make clippy and rustfmt a bit happier * replace unecessary if-else with unwrap_or and display which path could not get canonicalized if it fails at startup * canonicalize config_path to avoid crash when config.toml changes --- build.rs | 4 ++-- components/config/src/config/languages.rs | 17 +------------- components/config/src/config/markup.rs | 8 +------ components/config/src/highlighting.rs | 2 +- components/library/src/content/mod.rs | 5 +--- components/library/src/content/section.rs | 9 ++++---- components/library/src/library.rs | 5 ++-- components/library/src/pagination/mod.rs | 6 ++--- components/library/src/taxonomies/mod.rs | 19 +++++++-------- components/link_checker/src/lib.rs | 2 +- components/rendering/benches/all.rs | 3 ++- components/rendering/src/markdown.rs | 28 +++++++++++------------ components/rendering/tests/common/mod.rs | 2 ++ components/rendering/tests/toc.rs | 1 - components/search/src/lib.rs | 2 +- components/site/src/feed.rs | 2 +- components/site/src/lib.rs | 6 ++--- components/site/src/link_checking.rs | 24 +++++++------------ components/templates/src/lib.rs | 8 +++---- components/utils/src/templates.rs | 2 +- src/cli.rs | 4 ++-- src/cmd/serve.rs | 8 +++---- src/console.rs | 4 ++-- src/main.rs | 28 +++++++++++------------ 24 files changed, 82 insertions(+), 117 deletions(-) diff --git a/build.rs b/build.rs index c59dff192f..40f11040bb 100644 --- a/build.rs +++ b/build.rs @@ -14,7 +14,7 @@ fn generate_pe_header() { } res.set_icon("docs/static/favicon.ico"); res.set("LegalCopyright", ©right); - res.compile().expect("Failed to compile Windows resources!"); + res.compile().expect("Failed to compile Windows resources!"); } fn main() { @@ -29,7 +29,7 @@ fn main() { { return; } - if cfg!(windows){ + if cfg!(windows) { generate_pe_header(); } } diff --git a/components/config/src/config/languages.rs b/components/config/src/config/languages.rs index 064b680399..ac953766d2 100644 --- a/components/config/src/config/languages.rs +++ b/components/config/src/config/languages.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::config::search; use crate::config::taxonomies; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(default)] pub struct LanguageOptions { /// Title of the site. Defaults to None @@ -31,21 +31,6 @@ pub struct LanguageOptions { pub translations: HashMap, } -impl Default for LanguageOptions { - fn default() -> Self { - LanguageOptions { - title: None, - description: None, - generate_feed: false, - feed_filename: String::new(), - build_search_index: false, - taxonomies: Vec::new(), - search: search::Search::default(), - translations: HashMap::new(), - } - } -} - /// We want to ensure the language codes are valid ones pub fn validate_code(code: &str) -> Result<()> { if LanguageIdentifier::from_bytes(code.as_bytes()).is_err() { diff --git a/components/config/src/config/markup.rs b/components/config/src/config/markup.rs index fcfc4f8a91..5800d244e9 100644 --- a/components/config/src/config/markup.rs +++ b/components/config/src/config/markup.rs @@ -13,7 +13,7 @@ use crate::highlighting::{CLASS_STYLE, THEME_SET}; pub const DEFAULT_HIGHLIGHT_THEME: &str = "base16-ocean-dark"; -#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(default)] pub struct ThemeCss { /// Which theme are we generating the CSS from @@ -22,12 +22,6 @@ pub struct ThemeCss { pub filename: String, } -impl Default for ThemeCss { - fn default() -> ThemeCss { - ThemeCss { theme: String::new(), filename: String::new() } - } -} - #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(default)] pub struct Markdown { diff --git a/components/config/src/highlighting.rs b/components/config/src/highlighting.rs index c51a74982e..03cfc95d19 100644 --- a/components/config/src/highlighting.rs +++ b/components/config/src/highlighting.rs @@ -1,8 +1,8 @@ +use libs::once_cell::sync::Lazy; use libs::syntect::dumps::from_binary; use libs::syntect::highlighting::{Theme, ThemeSet}; use libs::syntect::html::ClassStyle; use libs::syntect::parsing::{SyntaxReference, SyntaxSet}; -use libs::once_cell::sync::Lazy; use crate::config::Config; diff --git a/components/library/src/content/mod.rs b/components/library/src/content/mod.rs index 627c6395d0..ff1c9e90d8 100644 --- a/components/library/src/content/mod.rs +++ b/components/library/src/content/mod.rs @@ -54,10 +54,7 @@ pub fn find_related_assets(path: &Path, config: &Config, recursive: bool) -> Vec } if let Some(ref globset) = config.ignored_content_globset { - assets = assets - .into_iter() - .filter(|p| !globset.is_match(p)) - .collect(); + assets = assets.into_iter().filter(|p| !globset.is_match(p)).collect(); } assets diff --git a/components/library/src/content/section.rs b/components/library/src/content/section.rs index 6a8787c631..188bb3dbc8 100644 --- a/components/library/src/content/section.rs +++ b/components/library/src/content/section.rs @@ -228,7 +228,7 @@ impl Section { #[cfg(test)] mod tests { - use std::fs::{create_dir, File, create_dir_all}; + use std::fs::{create_dir, create_dir_all, File}; use std::io::Write; use std::path::{Path, PathBuf}; @@ -269,8 +269,10 @@ mod tests { let tmp_dir = tempdir().expect("create temp dir"); let path = tmp_dir.path(); let article_path = path.join("content/posts/with-assets"); - create_dir_all(path.join(&article_path).join("foo/bar/baz/quux")).expect("create nested temp dir"); - create_dir_all(path.join(&article_path).join("foo/baz/quux")).expect("create nested temp dir"); + create_dir_all(path.join(&article_path).join("foo/bar/baz/quux")) + .expect("create nested temp dir"); + create_dir_all(path.join(&article_path).join("foo/baz/quux")) + .expect("create nested temp dir"); let mut f = File::create(article_path.join("_index.md")).unwrap(); f.write_all(b"+++\nslug=\"hey\"\n+++\n").unwrap(); File::create(article_path.join("example.js")).unwrap(); @@ -278,7 +280,6 @@ mod tests { File::create(article_path.join("fail.png")).unwrap(); File::create(article_path.join("foo/bar/baz/quux/quo.xlsx")).unwrap(); File::create(article_path.join("foo/bar/baz/quux/quo.docx")).unwrap(); - let mut gsb = GlobSetBuilder::new(); gsb.add(Glob::new("*.{js,png}").unwrap()); diff --git a/components/library/src/library.rs b/components/library/src/library.rs index 1a10a9a2f0..1128160f21 100644 --- a/components/library/src/library.rs +++ b/components/library/src/library.rs @@ -203,8 +203,7 @@ impl Library { section.pages.push(key); parent_is_transparent = section.meta.transparent; } - page.ancestors = - ancestors.get(&parent_section_path).cloned().unwrap_or_else(Vec::new); + page.ancestors = ancestors.get(&parent_section_path).cloned().unwrap_or_default(); // Don't forget to push the actual parent page.ancestors.push(*section_key); @@ -257,7 +256,7 @@ impl Library { children.sort_by(|a, b| sections_weight[a].cmp(§ions_weight[b])); section.subsections = children; } - section.ancestors = ancestors.get(§ion.file.path).cloned().unwrap_or_else(Vec::new); + section.ancestors = ancestors.get(§ion.file.path).cloned().unwrap_or_default(); } } diff --git a/components/library/src/pagination/mod.rs b/components/library/src/pagination/mod.rs index 99b059a912..fab63c3b52 100644 --- a/components/library/src/pagination/mod.rs +++ b/components/library/src/pagination/mod.rs @@ -99,10 +99,8 @@ impl<'a> Paginator<'a> { let paginate_by = taxonomy.kind.paginate_by.unwrap(); // Check for taxon-specific template, or use generic as fallback. let specific_template = format!("{}/single.html", taxonomy.kind.name); - let template = match check_template_fallbacks(&specific_template, tera, theme) { - Some(template) => template, - None => "taxonomy_single.html", - }; + let template = check_template_fallbacks(&specific_template, tera, theme) + .unwrap_or("taxonomy_single.html"); let mut paginator = Paginator { all_pages: Cow::Borrowed(&item.pages), pagers: Vec::with_capacity(item.pages.len() / paginate_by), diff --git a/components/library/src/taxonomies/mod.rs b/components/library/src/taxonomies/mod.rs index 241f7b9847..42e4f55c9d 100644 --- a/components/library/src/taxonomies/mod.rs +++ b/components/library/src/taxonomies/mod.rs @@ -204,12 +204,15 @@ impl Taxonomy { // Check for taxon-specific template, or use generic as fallback. let specific_template = format!("{}/single.html", self.kind.name); - let template = match check_template_fallbacks(&specific_template, tera, &config.theme) { - Some(template) => template, - None => "taxonomy_single.html", + let template = if let Some(template) = + check_template_fallbacks(&specific_template, tera, &config.theme) + { + template + } else { + "taxonomy_single.html" }; - render_template(&template, tera, context, &config.theme).map_err(|e| { + render_template(template, tera, context, &config.theme).map_err(|e| { Error::chain(format!("Failed to render single term {} page.", self.kind.name), e) }) } @@ -232,12 +235,10 @@ impl Taxonomy { // Check for taxon-specific template, or use generic as fallback. let specific_template = format!("{}/list.html", self.kind.name); - let template = match check_template_fallbacks(&specific_template, tera, &config.theme) { - Some(template) => template, - None => "taxonomy_list.html", - }; + let template = check_template_fallbacks(&specific_template, tera, &config.theme) + .unwrap_or("taxonomy_list.html"); - render_template(&template, tera, context, &config.theme).map_err(|e| { + render_template(template, tera, context, &config.theme).map_err(|e| { Error::chain(format!("Failed to render a list of {} page.", self.kind.name), e) }) } diff --git a/components/link_checker/src/lib.rs b/components/link_checker/src/lib.rs index 8baadce519..12af01dc52 100644 --- a/components/link_checker/src/lib.rs +++ b/components/link_checker/src/lib.rs @@ -106,7 +106,7 @@ fn check_page_for_anchor(url: &str, body: String) -> errors::Result<()> { let index = url.find('#').unwrap(); let anchor = url.get(index + 1..).unwrap(); - if has_anchor_id(&body, &anchor) { + if has_anchor_id(&body, anchor) { Ok(()) } else { Err(errors::Error::from(format!("Anchor `#{}` not found on page", anchor))) diff --git a/components/rendering/benches/all.rs b/components/rendering/benches/all.rs index 2c30bfb3f9..fc3e08fe92 100644 --- a/components/rendering/benches/all.rs +++ b/components/rendering/benches/all.rs @@ -5,8 +5,8 @@ use std::collections::HashMap; use config::Config; use front_matter::InsertAnchor; +use libs::tera::Tera; use rendering::{render_content, RenderContext}; -use tera::Tera; static CONTENT: &str = r#" # Modus cognitius profanam ne duae virtutis mundi @@ -118,6 +118,7 @@ fn bench_render_content_without_highlighting(b: &mut test::Bencher) { b.iter(|| render_content(CONTENT, &context).unwrap()); } +#[bench] fn bench_render_content_no_shortcode(b: &mut test::Bencher) { let tera = Tera::default(); let content2 = CONTENT.replace(r#"{{ youtube(id="my_youtube_id") }}"#, ""); diff --git a/components/rendering/src/markdown.rs b/components/rendering/src/markdown.rs index a323a0ac7c..03442b0fae 100644 --- a/components/rendering/src/markdown.rs +++ b/components/rendering/src/markdown.rs @@ -17,7 +17,7 @@ use crate::shortcode::{Shortcode, SHORTCODE_PLACEHOLDER}; const CONTINUE_READING: &str = ""; const ANCHOR_LINK_TEMPLATE: &str = "anchor-link.html"; -static EMOJI_REPLACER: Lazy = Lazy::new(|| EmojiReplacer::new()); +static EMOJI_REPLACER: Lazy = Lazy::new(EmojiReplacer::new); #[derive(Debug)] pub struct Rendered { @@ -92,23 +92,21 @@ fn fix_link( return Err(format!("Relative link {} not found.", link).into()); } } - } else { - if is_external_link(link) { - external_links.push(link.to_owned()); - link.to_owned() - } else if link == "#" { - link.to_string() - } else if link.starts_with("#") { - // local anchor without the internal zola path - if let Some(current_path) = context.current_page_path { - internal_links.push((current_path.to_owned(), Some(link[1..].to_owned()))); - format!("{}{}", context.current_page_permalink, &link) - } else { - link.to_string() - } + } else if is_external_link(link) { + external_links.push(link.to_owned()); + link.to_owned() + } else if link == "#" { + link.to_string() + } else if let Some(stripped_link) = link.strip_prefix('#') { + // local anchor without the internal zola path + if let Some(current_path) = context.current_page_path { + internal_links.push((current_path.to_owned(), Some(stripped_link.to_owned()))); + format!("{}{}", context.current_page_permalink, &link) } else { link.to_string() } + } else { + link.to_string() }; Ok(result) diff --git a/components/rendering/tests/common/mod.rs b/components/rendering/tests/common/mod.rs index e086744fba..3613b6983b 100644 --- a/components/rendering/tests/common/mod.rs +++ b/components/rendering/tests/common/mod.rs @@ -1,3 +1,5 @@ +#![allow(dead_code)] + pub struct ShortCode { pub name: &'static str, pub output: &'static str, diff --git a/components/rendering/tests/toc.rs b/components/rendering/tests/toc.rs index ecf05e3fe0..0076678401 100644 --- a/components/rendering/tests/toc.rs +++ b/components/rendering/tests/toc.rs @@ -1,6 +1,5 @@ mod common; -use common::ShortCode; use libs::tera; use rendering::Heading; diff --git a/components/search/src/lib.rs b/components/search/src/lib.rs index 2a7d1eba22..daaedc4532 100644 --- a/components/search/src/lib.rs +++ b/components/search/src/lib.rs @@ -111,7 +111,7 @@ fn fill_index( // TODO: fix it like the truncate in Tera match body.char_indices().nth(truncate_len) { None => row.push(body), - Some((idx, _)) => row.push((&body[..idx]).to_string()), + Some((idx, _)) => row.push((body[..idx]).to_string()), }; } else { row.push(body); diff --git a/components/site/src/feed.rs b/components/site/src/feed.rs index 1413acecb1..06f7e99a5f 100644 --- a/components/site/src/feed.rs +++ b/components/site/src/feed.rs @@ -54,7 +54,7 @@ pub fn render_feed( ); let library = site.library.read().unwrap(); // limit to the last n elements if the limit is set; otherwise use all. - let num_entries = site.config.feed_limit.unwrap_or_else(|| pages.len()); + let num_entries = site.config.feed_limit.unwrap_or(pages.len()); let p = pages.iter().take(num_entries).map(|x| x.to_serialized_basic(&library)).collect::>(); diff --git a/components/site/src/lib.rs b/components/site/src/lib.rs index 548f6a41e7..8f99b505ea 100644 --- a/components/site/src/lib.rs +++ b/components/site/src/lib.rs @@ -72,7 +72,7 @@ impl Site { pub fn new, P2: AsRef>(path: P, config_file: P2) -> Result { let path = path.as_ref(); let config_file = config_file.as_ref(); - let mut config = get_config(config_file)?; + let mut config = get_config(&path.join(config_file))?; if let Some(theme) = config.theme.clone() { // Grab data from the extra section of the theme @@ -163,7 +163,7 @@ impl Site { /// Reads all .md files in the `content` directory and create pages/sections /// out of them pub fn load(&mut self) -> Result<()> { - let base_path = self.base_path.to_string_lossy().replace("\\", "/"); + let base_path = self.base_path.to_string_lossy().replace('\\', "/"); self.library = Arc::new(RwLock::new(Library::new(0, 0, self.config.is_multilingual()))); let mut pages_insert_anchors = HashMap::new(); @@ -714,7 +714,7 @@ impl Site { let p = self.static_path.join(&t.filename); if !p.exists() { let content = &self.config.markdown.export_theme_css(&t.theme); - create_file(&p, &content)?; + create_file(&p, content)?; } } diff --git a/components/site/src/link_checking.rs b/components/site/src/link_checking.rs index e22e684fa9..3437b40d0b 100644 --- a/components/site/src/link_checking.rs +++ b/components/site/src/link_checking.rs @@ -17,22 +17,14 @@ pub fn check_internal_links_with_anchors(site: &Site) -> Result<()> { let library = site.library.write().expect("Get lock for check_internal_links_with_anchors"); // Chain all internal links, from both sections and pages. - let page_links = library - .pages() - .values() - .map(|p| { - let path = &p.file.path; - p.internal_links.iter().map(move |l| (path.clone(), l)) - }) - .flatten(); - let section_links = library - .sections() - .values() - .map(|p| { - let path = &p.file.path; - p.internal_links.iter().map(move |l| (path.clone(), l)) - }) - .flatten(); + let page_links = library.pages().values().flat_map(|p| { + let path = &p.file.path; + p.internal_links.iter().map(move |l| (path.clone(), l)) + }); + let section_links = library.sections().values().flat_map(|p| { + let path = &p.file.path; + p.internal_links.iter().map(move |l| (path.clone(), l)) + }); let all_links = page_links.chain(section_links); // Only keep links with anchor fragments, and count them too. diff --git a/components/templates/src/lib.rs b/components/templates/src/lib.rs index 735fee3928..750c431307 100644 --- a/components/templates/src/lib.rs +++ b/components/templates/src/lib.rs @@ -53,7 +53,7 @@ pub fn render_redirect_template(url: &str, tera: &Tera) -> Result { pub fn load_tera(path: &Path, config: &Config) -> Result { let tpl_glob = - format!("{}/{}", path.to_string_lossy().replace("\\", "/"), "templates/**/*.{*ml,md}"); + format!("{}/{}", path.to_string_lossy().replace('\\', "/"), "templates/**/*.{*ml,md}"); // Only parsing as we might be extending templates from themes and that would error // as we haven't loaded them yet @@ -68,9 +68,9 @@ pub fn load_tera(path: &Path, config: &Config) -> Result { } let theme_tpl_glob = format!( - "{}/{}", - path.to_string_lossy().replace("\\", "/"), - format!("themes/{}/templates/**/*.{{*ml,md}}", theme) + "{}/themes/{}/templates/**/*.{{*ml,md}}", + path.to_string_lossy().replace('\\', "/"), + theme ); let mut tera_theme = Tera::parse(&theme_tpl_glob) .map_err(|e| Error::chain("Error parsing templates from themes", e))?; diff --git a/components/utils/src/templates.rs b/components/utils/src/templates.rs index 10a2242918..25d508e8d9 100644 --- a/components/utils/src/templates.rs +++ b/components/utils/src/templates.rs @@ -78,7 +78,7 @@ pub fn render_template( theme: &Option, ) -> Result { if let Some(template) = check_template_fallbacks(name, tera, theme) { - return tera.render(&template, &context).map_err(std::convert::Into::into); + return tera.render(template, &context).map_err(std::convert::Into::into); } // maybe it's a default one? diff --git a/src/cli.rs b/src/cli.rs index 43daf73769..82b1f9084e 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -10,8 +10,8 @@ pub struct Cli { pub root: PathBuf, /// Path to a config file other than config.toml in the root of project - #[clap(short = 'c', long)] - pub config: Option, + #[clap(short = 'c', long, default_value = "config.toml")] + pub config: PathBuf, #[clap(subcommand)] pub command: Command, diff --git a/src/cmd/serve.rs b/src/cmd/serve.rs index fe191f610b..8a488cd7a2 100644 --- a/src/cmd/serve.rs +++ b/src/cmd/serve.rs @@ -310,7 +310,8 @@ pub fn serve( } let config_path = PathBuf::from(config_file); - let config_path_rel = diff_paths(&config_path, &root_dir).unwrap_or(config_path.clone()); + let config_path_rel = + diff_paths(&config_path, &root_dir).unwrap_or_else(|| config_path.clone()); // An array of (path, WatchMode) where the path should be watched for changes, // and the WatchMode value indicates whether this file/folder must exist for @@ -528,10 +529,7 @@ pub fn serve( if path.is_dir() && is_folder_empty(&path) { continue; } - println!( - "Change detected @ {}", - Local::now().format("%Y-%m-%d %H:%M:%S").to_string() - ); + println!("Change detected @ {}", Local::now().format("%Y-%m-%d %H:%M:%S")); let start = Instant::now(); match detect_change_kind(root_dir, &path, &config_path) { diff --git a/src/console.rs b/src/console.rs index 14312ce00f..077a913a93 100644 --- a/src/console.rs +++ b/src/console.rs @@ -36,9 +36,9 @@ pub fn error(message: &str) { fn colorize(message: &str, color: &ColorSpec) { let mut stdout = StandardStream::stdout(*COLOR_CHOICE); stdout.set_color(color).unwrap(); - write!(&mut stdout, "{}", message).unwrap(); + write!(stdout, "{}", message).unwrap(); stdout.set_color(&ColorSpec::new()).unwrap(); - writeln!(&mut stdout).unwrap(); + writeln!(stdout).unwrap(); } /// Display in the console the number of pages/sections in the site diff --git a/src/main.rs b/src/main.rs index 764d9e3208..acff8178ce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +use std::path::PathBuf; use std::time::Instant; use cli::{Cli, Command}; @@ -12,17 +13,16 @@ mod prompt; fn main() { let cli = Cli::parse(); - let root_dir = cli - .root - .canonicalize() - .unwrap_or_else(|_| panic!("Cannot find root directory: {}", cli.root.display())); - let config_file = cli - .config - .map(|path| { - path.canonicalize() - .unwrap_or_else(|_| panic!("Cannot find config file: {}", path.display())) - }) - .unwrap_or_else(|| root_dir.join("config.toml")); + let cli_dir: PathBuf = + cli.root.canonicalize().unwrap_or_else(|_| panic!("Could not find canonical path of root dir: {}", cli.root.display())); + + let root_dir = cli_dir + .ancestors() + .find_map(|a| if a.join(&cli.config).exists() { Some(a) } else { None }) + .unwrap_or_else(|| panic!("could not find directory containing config file")); + + // if we got here we found root_dir so config file should exist so we can unwrap safely + let config_file = root_dir.join(&cli.config).canonicalize().unwrap(); match cli.command { Command::Init { name, force } => { @@ -35,7 +35,7 @@ fn main() { console::info("Building site..."); let start = Instant::now(); match cmd::build( - &root_dir, + root_dir, &config_file, base_url.as_deref(), output_dir.as_deref(), @@ -63,7 +63,7 @@ fn main() { console::info("Building site..."); if let Err(e) = cmd::serve( - &root_dir, + root_dir, &interface, port, output_dir.as_deref(), @@ -80,7 +80,7 @@ fn main() { Command::Check { drafts } => { console::info("Checking site..."); let start = Instant::now(); - match cmd::check(&root_dir, &config_file, None, None, drafts) { + match cmd::check(root_dir, &config_file, None, None, drafts) { Ok(()) => console::report_elapsed_time(start), Err(e) => { console::unravel_errors("Failed to check the site", &e);