summary history branches tags files
commit:b84065e5657b07ecd771a27b128e167ce961531a
author:Trevor Bentley
committer:Trevor Bentley
date:Thu Jan 12 17:45:09 2023 +0100
parents:65982202a12e0406c683960ae91a3547960c1044
apply rustfmt.  ugh.
diff --git a/rustfmt.toml b/rustfmt.toml
line changes: +1/-0
index 0000000..7530651
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+max_width = 120

diff --git a/src/generate.rs b/src/generate.rs
line changes: +255/-177
index e412fe2..966e3a5
--- a/src/generate.rs
+++ b/src/generate.rs
@@ -1,30 +1,15 @@
 use crate::{
-    error, normal, normal_noln, loud, louder, loudest,
-    git::{
-        dir_listing,
-        GitFile,
-        GitRepo,
-        GitsyMetadata,
-        parse_repo,
-    },
-    settings::{
-        GitsyCli,
-        GitsyRepoDescriptions,
-        GitsySettings,
-        GitsySettingsRepo,
-    },
-    template::{
-        DirFilter,
-        FileFilter,
-        TsDateFn,
-        TsTimestampFn,
-    },
+    error,
+    git::{dir_listing, parse_repo, GitFile, GitRepo, GitsyMetadata},
+    loud, louder, loudest, normal, normal_noln,
+    settings::{GitsyCli, GitsyRepoDescriptions, GitsySettings, GitsySettingsRepo},
+    template::{DirFilter, FileFilter, TsDateFn, TsTimestampFn},
     util::GitsyError,
 };
-use git2::{Repository, Error};
+use git2::{Error, Repository};
 use rayon::prelude::*;
 use std::cmp;
-use std::fs::{File, create_dir};
+use std::fs::{create_dir, File};
 use std::io::Write;
 use std::path::{Path, PathBuf};
 use std::sync::atomic::{AtomicUsize, Ordering};
@@ -36,9 +21,9 @@ use pulldown_cmark::{html, Options, Parser as MdParser};
 
 #[cfg(any(feature = "highlight", feature = "highlight_fast"))]
 use syntect::{
-    html::{ClassedHTMLGenerator, ClassStyle, css_for_theme_with_class_style},
-    parsing::SyntaxSet,
     highlighting::ThemeSet,
+    html::{css_for_theme_with_class_style, ClassStyle, ClassedHTMLGenerator},
+    parsing::SyntaxSet,
     util::LinesWithEndings,
 };
 
@@ -52,7 +37,7 @@ macro_rules! size_check {
         if total.saturating_add($cur) > $settings.limit_total_size.unwrap_or(usize::MAX) {
             $action;
         }
-    }
+    };
 }
 
 pub struct GitsyGenerator {
@@ -62,8 +47,7 @@ pub struct GitsyGenerator {
 }
 
 impl GitsyGenerator {
-    pub fn new(cli: GitsyCli, settings: GitsySettings,
-               repo_descriptions: GitsyRepoDescriptions) -> GitsyGenerator {
+    pub fn new(cli: GitsyCli, settings: GitsySettings, repo_descriptions: GitsyRepoDescriptions) -> GitsyGenerator {
         GitsyGenerator {
             cli,
             settings,
@@ -86,16 +70,18 @@ impl GitsyGenerator {
         let syntax_set = SyntaxSet::load_defaults_newlines();
         let syntax = match syntax_set.find_syntax_by_extension(extension) {
             Some(s) => s,
-            _ => { return contents.to_string(); },
+            _ => {
+                return contents.to_string();
+            }
         };
         let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced);
         for line in LinesWithEndings::from(contents) {
             match html_generator.parse_html_for_line_which_includes_newline(line) {
-                Ok(_) => {},
+                Ok(_) => {}
                 Err(_) => {
                     error!("Warning: failed to apply syntax highlighting.");
                     return contents.to_string();
-                },
+                }
             }
         }
         html_generator.finalize()
@@ -115,18 +101,22 @@ impl GitsyGenerator {
                             loudest!(" - rendering Markdown in {}", path.display());
                             let (cstr, rendered, pre) = (GitsyGenerator::parse_markdown(&cstr), true, false);
                             (cstr, rendered, pre)
-                        },
+                        }
                         #[cfg(any(feature = "highlight", feature = "highlight_fast"))]
                         Some(x) if settings.syntax_highlight.unwrap_or(false) => {
                             loudest!(" - syntax highlighting {}", path.display());
-                            (GitsyGenerator::syntax_highlight(&cstr, x.to_string_lossy().to_string().as_str()), true, true)
-                        },
+                            (
+                                GitsyGenerator::syntax_highlight(&cstr, x.to_string_lossy().to_string().as_str()),
+                                true,
+                                true,
+                            )
+                        }
                         _ => (cstr, false, true),
                     };
                     file.contents_safe = rendered;
                     file.contents_preformatted = pre;
                     Some(content)
-                },
+                }
                 true => Some(format!("[Binary data ({} bytes)]", blob.content().len())),
             };
         }
@@ -134,8 +124,7 @@ impl GitsyGenerator {
     }
 
     fn write_rendered(path: &str, rendered: &str) -> usize {
-        let mut file = File::create(path)
-            .expect(&format!("Unable to write to output path: {}", path));
+        let mut file = File::create(path).expect(&format!("Unable to write to output path: {}", path));
         file.write(rendered.as_bytes())
             .expect(&format!("Failed to save rendered html to path: {}", path));
         louder!(" - wrote file: {}", path);
@@ -147,10 +136,10 @@ impl GitsyGenerator {
         template_path.push("**");
         template_path.push("*.html");
         let mut tera = Tera::new(&template_path.to_string_lossy().to_string())?;
-        tera.register_filter("only_files", FileFilter{});
-        tera.register_filter("only_dirs", DirFilter{});
-        tera.register_function("ts_to_date", TsDateFn{});
-        tera.register_function("ts_to_git_timestamp", TsTimestampFn{});
+        tera.register_filter("only_files", FileFilter {});
+        tera.register_filter("only_dirs", DirFilter {});
+        tera.register_function("ts_to_date", TsDateFn {});
+        tera.register_function("ts_to_git_timestamp", TsTimestampFn {});
         Ok(tera)
     }
 
@@ -164,22 +153,31 @@ impl GitsyGenerator {
         let generated_dt = chrono::offset::Local::now();
         let mut global_bytes = 0;
         let mut total_bytes = 0;
-        let mut repos: Vec<GitRepo> = vec!();
+        let mut repos: Vec<GitRepo> = vec![];
 
         if self.repo_descriptions.len() == 0 {
-            panic!("No Git repositories defined!  Please check your configuration file ({})",
-                   self.cli.path.display());
+            panic!(
+                "No Git repositories defined!  Please check your configuration file ({})",
+                self.cli.path.display()
+            );
         }
 
         // Sort the repositories by name
         let mut repo_vec: Vec<GitsySettingsRepo> = self.repo_descriptions.iter().cloned().collect();
-        repo_vec.sort_by(|x,y| x.name.as_deref().map(|n| n.cmp(&y.name.as_deref().unwrap_or_default()))
-                         .unwrap_or(cmp::Ordering::Equal));
+        repo_vec.sort_by(|x, y| {
+            x.name
+                .as_deref()
+                .map(|n| n.cmp(&y.name.as_deref().unwrap_or_default()))
+                .unwrap_or(cmp::Ordering::Equal)
+        });
         // Find the one with the longest name, for pretty printing
         let global_name = "repo list";
-        let longest_repo_name = repo_vec.iter().fold(0, |acc, x| {
-            cmp::max(acc, x.name.as_deref().map(|n| n.len()).unwrap_or(0))
-        }).max(global_name.len());
+        let longest_repo_name = repo_vec
+            .iter()
+            .fold(0, |acc, x| {
+                cmp::max(acc, x.name.as_deref().map(|n| n.len()).unwrap_or(0))
+            })
+            .max(global_name.len());
 
         loudest!("Global settings:\n{:#?}", &self.settings);
 
@@ -191,78 +189,89 @@ impl GitsyGenerator {
             let name = repo_desc.name.as_deref().expect("A configured repository has no name!");
 
             let repo_path = match &repo_desc.path {
-                url if url.starts_with("https://") ||
-                    url.to_str().unwrap_or_default().contains("@") => {
-                        if self.settings.outputs.cloned_repos.is_none() {
-                            error!("ERROR: Found remote repo [{}], but `cloned_repos` directory not configured.", name);
-                            continue;
-                        };
-                        let clone_path: PathBuf = [self.settings.outputs.cloned_repos.as_deref().unwrap(),
-                                                   name].iter().collect();
-                        match Repository::open(&clone_path) {
-                            Ok(r) => {
-                                // Repo already cloned, so update all refs
-                                let refs: Vec<String> = r.references()
-                                    .expect(&format!("Unable to enumerate references for repo [{}]", name))
-                                    .map(|x| x.expect(&format!("Found invalid reference in repo [{}]", name))
-                                         .name()
-                                         .expect(&format!("Found unnamed reference in repo: [{}]", name))
-                                         .to_string()).collect();
-                                r.find_remote("origin")
-                                    .expect(&format!("Clone of repo [{}] missing `origin` remote.", name))
-                                    .fetch(&refs, None, None)
-                                    .expect(&format!("Failed to fetch updates from remote repo [{}]", name));
-                                clone_path.to_string_lossy().to_string()
-                            },
-                            Err(_) => {
-                                let mut builder = git2::build::RepoBuilder::new();
-
-                                // TODO: git2-rs's ssh support just doesn't seem to
-                                // work.  It finds the repo, but fails to either
-                                // decrypt or use the private key.
-                                //
-                                //if !url.starts_with("https://") {
-                                //    use secrecy::ExposeSecret;
-                                //    // this must be SSH, which needs credentials.
-                                //    let mut callbacks = git2::RemoteCallbacks::new();
-                                //    callbacks.credentials(|_url, username_from_url, _allowed_types| {
-                                //        //git2::Cred::ssh_key_from_agent(username_from_url.unwrap())
-                                //
-                                //        let keyfile = format!("{}/.ssh/id_rsa", std::env::var("HOME").unwrap());
-                                //        let passphrase = pinentry::PassphraseInput::with_default_binary().unwrap()
-                                //            .with_description(&format!("Enter passphrase for SSH key {} (repo: {})",
-                                //                                       keyfile, url.display()))
-                                //            .with_prompt("Passphrase:")
-                                //            .interact().unwrap();
-                                //        git2::Cred::ssh_key(
-                                //            username_from_url.unwrap(),
-                                //            None,
-                                //            Path::new(&keyfile),
-                                //            Some(passphrase.expose_secret()),
-                                //        )
-                                //    });
-                                //    let mut options = git2::FetchOptions::new();
-                                //    options.remote_callbacks(callbacks);
-                                //    builder.fetch_options(options);
-                                //}
-                                builder
-                                    .bare(true)
-                                    .clone(&url.to_string_lossy().to_string(), &clone_path)
-                                    .expect(&format!("Failed to clone remote repo [{}]", name));
-                                clone_path.to_string_lossy().to_string()
-                            }
+                url if url.starts_with("https://") || url.to_str().unwrap_or_default().contains("@") => {
+                    if self.settings.outputs.cloned_repos.is_none() {
+                        error!(
+                            "ERROR: Found remote repo [{}], but `cloned_repos` directory not configured.",
+                            name
+                        );
+                        continue;
+                    };
+                    let clone_path: PathBuf = [self.settings.outputs.cloned_repos.as_deref().unwrap(), name]
+                        .iter()
+                        .collect();
+                    match Repository::open(&clone_path) {
+                        Ok(r) => {
+                            // Repo already cloned, so update all refs
+                            let refs: Vec<String> = r
+                                .references()
+                                .expect(&format!("Unable to enumerate references for repo [{}]", name))
+                                .map(|x| {
+                                    x.expect(&format!("Found invalid reference in repo [{}]", name))
+                                        .name()
+                                        .expect(&format!("Found unnamed reference in repo: [{}]", name))
+                                        .to_string()
+                                })
+                                .collect();
+                            r.find_remote("origin")
+                                .expect(&format!("Clone of repo [{}] missing `origin` remote.", name))
+                                .fetch(&refs, None, None)
+                                .expect(&format!("Failed to fetch updates from remote repo [{}]", name));
+                            clone_path.to_string_lossy().to_string()
+                        }
+                        Err(_) => {
+                            let mut builder = git2::build::RepoBuilder::new();
+
+                            // TODO: git2-rs's ssh support just doesn't seem to
+                            // work.  It finds the repo, but fails to either
+                            // decrypt or use the private key.
+                            //
+                            //if !url.starts_with("https://") {
+                            //    use secrecy::ExposeSecret;
+                            //    // this must be SSH, which needs credentials.
+                            //    let mut callbacks = git2::RemoteCallbacks::new();
+                            //    callbacks.credentials(|_url, username_from_url, _allowed_types| {
+                            //        //git2::Cred::ssh_key_from_agent(username_from_url.unwrap())
+                            //
+                            //        let keyfile = format!("{}/.ssh/id_rsa", std::env::var("HOME").unwrap());
+                            //        let passphrase = pinentry::PassphraseInput::with_default_binary().unwrap()
+                            //            .with_description(&format!("Enter passphrase for SSH key {} (repo: {})",
+                            //                                       keyfile, url.display()))
+                            //            .with_prompt("Passphrase:")
+                            //            .interact().unwrap();
+                            //        git2::Cred::ssh_key(
+                            //            username_from_url.unwrap(),
+                            //            None,
+                            //            Path::new(&keyfile),
+                            //            Some(passphrase.expose_secret()),
+                            //        )
+                            //    });
+                            //    let mut options = git2::FetchOptions::new();
+                            //    options.remote_callbacks(callbacks);
+                            //    builder.fetch_options(options);
+                            //}
+                            builder
+                                .bare(true)
+                                .clone(&url.to_string_lossy().to_string(), &clone_path)
+                                .expect(&format!("Failed to clone remote repo [{}]", name));
+                            clone_path.to_string_lossy().to_string()
                         }
                     }
+                }
                 dir => {
                     match dir.metadata() {
-                        Ok(m) if m.is_dir() => {},
+                        Ok(m) if m.is_dir() => {}
                         _ => {
-                            error!("ERROR: local repository [{}]: directory not found: {}", name, dir.display());
+                            error!(
+                                "ERROR: local repository [{}]: directory not found: {}",
+                                name,
+                                dir.display()
+                            );
                             continue;
-                        },
+                        }
                     }
                     dir.to_string_lossy().to_string()
-                },
+                }
             };
             let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
             let metadata = GitsyMetadata {
@@ -277,7 +286,9 @@ impl GitsyGenerator {
 
             let mut local_ctx = Context::from_serialize(&summary).unwrap();
             if let Some(extra) = &self.settings.extra {
-                local_ctx.try_insert("extra", extra).expect("Failed to add extra settings to template engine.");
+                local_ctx
+                    .try_insert("extra", extra)
+                    .expect("Failed to add extra settings to template engine.");
             }
             if let Some(site_name) = &self.settings.site_name {
                 local_ctx.insert("site_name", site_name);
@@ -294,8 +305,11 @@ impl GitsyGenerator {
             if let Some(templ_file) = self.settings.templates.repo_summary.as_deref() {
                 match tera.render(templ_file, &local_ctx) {
                     Ok(rendered) => {
-                        repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.repo_summary(Some(&summary), None), &rendered);
-                    },
+                        repo_bytes += GitsyGenerator::write_rendered(
+                            &self.settings.outputs.repo_summary(Some(&summary), None),
+                            &rendered,
+                        );
+                    }
                     Err(x) => match x.kind {
                         _ => error!("ERROR: {:?}", x),
                     },
@@ -308,8 +322,11 @@ impl GitsyGenerator {
                 if let Some(templ_file) = self.settings.templates.branch.as_deref() {
                     match tera.render(templ_file, &local_ctx) {
                         Ok(rendered) => {
-                            repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.branch(Some(&summary), Some(branch)), &rendered);
-                        },
+                            repo_bytes += GitsyGenerator::write_rendered(
+                                &self.settings.outputs.branch(Some(&summary), Some(branch)),
+                                &rendered,
+                            );
+                        }
                         Err(x) => match x.kind {
                             _ => error!("ERROR: {:?}", x),
                         },
@@ -329,8 +346,11 @@ impl GitsyGenerator {
                 if let Some(templ_file) = self.settings.templates.tag.as_deref() {
                     match tera.render(templ_file, &local_ctx) {
                         Ok(rendered) => {
-                            repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.tag(Some(&summary), Some(tag)), &rendered);
-                        },
+                            repo_bytes += GitsyGenerator::write_rendered(
+                                &self.settings.outputs.tag(Some(&summary), Some(tag)),
+                                &rendered,
+                            );
+                        }
                         Err(x) => match x.kind {
                             _ => error!("ERROR: {:?}", x),
                         },
@@ -342,12 +362,17 @@ impl GitsyGenerator {
 
             for (_id, commit) in &summary.commits {
                 size_check!(repo_desc, repo_bytes, total_bytes, break);
-                local_ctx.try_insert("commit", &commit).expect("Failed to add commit to template engine.");
+                local_ctx
+                    .try_insert("commit", &commit)
+                    .expect("Failed to add commit to template engine.");
                 if let Some(templ_file) = self.settings.templates.commit.as_deref() {
                     match tera.render(templ_file, &local_ctx) {
                         Ok(rendered) => {
-                            repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.commit(Some(&summary), Some(commit)), &rendered);
-                        },
+                            repo_bytes += GitsyGenerator::write_rendered(
+                                &self.settings.outputs.commit(Some(&summary), Some(commit)),
+                                &rendered,
+                            );
+                        }
                         Err(x) => match x.kind {
                             _ => error!("ERROR: {:?}", x),
                         },
@@ -359,44 +384,65 @@ impl GitsyGenerator {
             #[cfg(any(feature = "highlight", feature = "highlight_fast"))]
             if self.settings.templates.file.is_some() {
                 let ts = ThemeSet::load_defaults();
-                let theme = ts.themes.get(repo_desc.syntax_highlight_theme.as_deref()
-                                          .unwrap_or("base16-ocean.light")).expect("Invalid syntax highlighting theme specified.");
+                let theme = ts
+                    .themes
+                    .get(
+                        repo_desc
+                            .syntax_highlight_theme
+                            .as_deref()
+                            .unwrap_or("base16-ocean.light"),
+                    )
+                    .expect("Invalid syntax highlighting theme specified.");
                 let css: String = css_for_theme_with_class_style(theme, syntect::html::ClassStyle::Spaced)
                     .expect("Invalid syntax highlighting theme specified.");
-                repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.syntax_css(Some(&summary), None), css.as_str());
+                repo_bytes += GitsyGenerator::write_rendered(
+                    &self.settings.outputs.syntax_css(Some(&summary), None),
+                    css.as_str(),
+                );
             }
 
-
             // TODO: parallelize the rest of the processing steps.  This one is
             // done first because syntax highlighting is very slow.
             let files: Vec<&GitFile> = summary.all_files.iter().filter(|x| x.kind == "file").collect();
             let atomic_bytes: AtomicUsize = AtomicUsize::new(repo_bytes);
-            let _ = files.par_iter().fold(|| Some(0), |acc, file| {
-                // These two have to be recreated.  Cloning the Tera context is expensive.
-                let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
-                let mut local_ctx = local_ctx.clone();
-
-                let mut local_bytes = 0;
-                let cur_repo_bytes = atomic_bytes.load(Ordering::Relaxed);
-                size_check!(repo_desc, cur_repo_bytes, total_bytes, return None);
-                let file = match file.size < repo_desc.limit_file_size.unwrap_or(usize::MAX) {
-                    true => GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc).expect("Failed to parse file."),
-                    false => (*file).clone(),
-                };
-                local_ctx.try_insert("file", &file).expect("Failed to add file to template engine.");
-                if let Some(templ_file) = self.settings.templates.file.as_deref() {
-                    match tera.render(templ_file, &local_ctx) {
-                        Ok(rendered) => {
-                            local_bytes = GitsyGenerator::write_rendered(&self.settings.outputs.file(Some(&summary), Some(&file)), &rendered);
-                            atomic_bytes.fetch_add(local_bytes, Ordering::Relaxed);
-                        },
-                        Err(x) => match x.kind {
-                            _ => error!("ERROR: {:?}", x),
-                        },
-                    }
-                }
-                local_ctx.remove("file");
-                Some(acc.unwrap() + local_bytes)})
+            let _ = files
+                .par_iter()
+                .fold(
+                    || Some(0),
+                    |acc, file| {
+                        // These two have to be recreated.  Cloning the Tera context is expensive.
+                        let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
+                        let mut local_ctx = local_ctx.clone();
+
+                        let mut local_bytes = 0;
+                        let cur_repo_bytes = atomic_bytes.load(Ordering::Relaxed);
+                        size_check!(repo_desc, cur_repo_bytes, total_bytes, return None);
+                        let file = match file.size < repo_desc.limit_file_size.unwrap_or(usize::MAX) {
+                            true => GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc)
+                                .expect("Failed to parse file."),
+                            false => (*file).clone(),
+                        };
+                        local_ctx
+                            .try_insert("file", &file)
+                            .expect("Failed to add file to template engine.");
+                        if let Some(templ_file) = self.settings.templates.file.as_deref() {
+                            match tera.render(templ_file, &local_ctx) {
+                                Ok(rendered) => {
+                                    local_bytes = GitsyGenerator::write_rendered(
+                                        &self.settings.outputs.file(Some(&summary), Some(&file)),
+                                        &rendered,
+                                    );
+                                    atomic_bytes.fetch_add(local_bytes, Ordering::Relaxed);
+                                }
+                                Err(x) => match x.kind {
+                                    _ => error!("ERROR: {:?}", x),
+                                },
+                            }
+                        }
+                        local_ctx.remove("file");
+                        Some(acc.unwrap() + local_bytes)
+                    },
+                )
                 .while_some() // allow short-circuiting if size limit is reached
                 .sum::<usize>();
             repo_bytes = atomic_bytes.load(Ordering::Relaxed);
@@ -407,12 +453,17 @@ impl GitsyGenerator {
                     continue;
                 }
                 let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
-                local_ctx.try_insert("files", &listing).expect("Failed to add dir to template engine.");
+                local_ctx
+                    .try_insert("files", &listing)
+                    .expect("Failed to add dir to template engine.");
                 if let Some(templ_file) = self.settings.templates.dir.as_deref() {
                     match tera.render(templ_file, &local_ctx) {
                         Ok(rendered) => {
-                            repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.dir(Some(&summary), Some(dir)), &rendered);
-                        },
+                            repo_bytes += GitsyGenerator::write_rendered(
+                                &self.settings.outputs.dir(Some(&summary), Some(dir)),
+                                &rendered,
+                            );
+                        }
                         Err(x) => match x.kind {
                             _ => error!("ERROR: {:?}", x),
                         },
@@ -426,12 +477,16 @@ impl GitsyGenerator {
                 for src_file in repo_desc.asset_files.as_ref().unwrap() {
                     let src_file = PathBuf::from(repo_path.to_owned() + "/" + src_file);
                     let mut dst_file = PathBuf::from(&target_dir);
-                    dst_file.push(src_file.file_name()
-                                  .expect(&format!("Failed to copy repo asset file: {} ({})",
-                                                   src_file.display(), repo_desc.name.as_deref().unwrap_or_default())));
-                    std::fs::copy(&src_file, &dst_file)
-                        .expect(&format!("Failed to copy repo asset file: {} ({})",
-                                         src_file.display(), repo_desc.name.as_deref().unwrap_or_default()));
+                    dst_file.push(src_file.file_name().expect(&format!(
+                        "Failed to copy repo asset file: {} ({})",
+                        src_file.display(),
+                        repo_desc.name.as_deref().unwrap_or_default()
+                    )));
+                    std::fs::copy(&src_file, &dst_file).expect(&format!(
+                        "Failed to copy repo asset file: {} ({})",
+                        src_file.display(),
+                        repo_desc.name.as_deref().unwrap_or_default()
+                    ));
                     if let Ok(meta) = std::fs::metadata(dst_file) {
                         repo_bytes += meta.len() as usize;
                     }
@@ -439,22 +494,33 @@ impl GitsyGenerator {
             }
 
             repos.push(summary);
-            normal!("{}done in {:.2}s ({} bytes)",
-                    match crate::util::VERBOSITY.load(Ordering::Relaxed) > 1 {
-                        true => " - ",
-                        _ => "",
-                    },
-                    start_repo.elapsed().as_secs_f32(), repo_bytes);
+            normal!(
+                "{}done in {:.2}s ({} bytes)",
+                match crate::util::VERBOSITY.load(Ordering::Relaxed) > 1 {
+                    true => " - ",
+                    _ => "",
+                },
+                start_repo.elapsed().as_secs_f32(),
+                repo_bytes
+            );
             total_bytes += repo_bytes;
             size_check!(repo_desc, 0, total_bytes, break); // break if total is exceeded
         }
 
         let start_global = Instant::now();
-        normal_noln!("[{}{}]... ", global_name, " ".repeat(longest_repo_name - global_name.len()));
+        normal_noln!(
+            "[{}{}]... ",
+            global_name,
+            " ".repeat(longest_repo_name - global_name.len())
+        );
         let mut global_ctx = Context::new();
-        global_ctx.try_insert("repos", &repos).expect("Failed to add repo to template engine.");
+        global_ctx
+            .try_insert("repos", &repos)
+            .expect("Failed to add repo to template engine.");
         if let Some(extra) = &self.settings.extra {
-            global_ctx.try_insert("extra", extra).expect("Failed to add extra settings to template engine.");
+            global_ctx
+                .try_insert("extra", extra)
+                .expect("Failed to add extra settings to template engine.");
         }
         if let Some(site_name) = &self.settings.site_name {
             global_ctx.insert("site_name", site_name);
@@ -471,8 +537,9 @@ impl GitsyGenerator {
         if let Some(templ_file) = self.settings.templates.repo_list.as_deref() {
             match tera.render(templ_file, &global_ctx) {
                 Ok(rendered) => {
-                    global_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.repo_list(None, None), &rendered);
-                },
+                    global_bytes +=
+                        GitsyGenerator::write_rendered(&self.settings.outputs.repo_list(None, None), &rendered);
+                }
                 Err(x) => match x.kind {
                     _ => error!("ERROR: {:?}", x),
                 },
@@ -483,7 +550,7 @@ impl GitsyGenerator {
             match tera.render(templ_file, &global_ctx) {
                 Ok(rendered) => {
                     global_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.error(None, None), &rendered);
-                },
+                }
                 Err(x) => match x.kind {
                     _ => error!("ERROR: {:?}", x),
                 },
@@ -495,8 +562,11 @@ impl GitsyGenerator {
             for src_file in self.settings.asset_files.as_ref().unwrap() {
                 let src_file = PathBuf::from(src_file);
                 let mut dst_file = PathBuf::from(&target_dir);
-                dst_file.push(src_file.file_name()
-                              .expect(&format!("Failed to copy asset file: {}", src_file.display())));
+                dst_file.push(
+                    src_file
+                        .file_name()
+                        .expect(&format!("Failed to copy asset file: {}", src_file.display())),
+                );
                 std::fs::copy(&src_file, &dst_file)
                     .expect(&format!("Failed to copy asset file: {}", src_file.display()));
                 if let Ok(meta) = std::fs::metadata(dst_file) {
@@ -506,8 +576,16 @@ impl GitsyGenerator {
         }
 
         total_bytes += global_bytes;
-        normal!("done in {:.2}s ({} bytes)", start_global.elapsed().as_secs_f32(), global_bytes);
-        loud!("Wrote {} bytes in {:.2}s", total_bytes, start_all.elapsed().as_secs_f32());
+        normal!(
+            "done in {:.2}s ({} bytes)",
+            start_global.elapsed().as_secs_f32(),
+            global_bytes
+        );
+        loud!(
+            "Wrote {} bytes in {:.2}s",
+            total_bytes,
+            start_all.elapsed().as_secs_f32()
+        );
         Ok(())
     }
 }

diff --git a/src/git.rs b/src/git.rs
line changes: +89/-50
index a9f73f1..fd0ad3f
--- a/src/git.rs
+++ b/src/git.rs
@@ -1,7 +1,7 @@
-use crate::{error, loud, loudest};
 use crate::settings::GitsySettingsRepo;
-use git2::{DiffOptions, Repository, Error};
-use serde::{Serialize, Deserialize};
+use crate::{error, loud, loudest};
+use git2::{DiffOptions, Error, Repository};
+use serde::{Deserialize, Serialize};
 use std::cell::RefCell;
 use std::collections::BTreeMap;
 use std::rc::Rc;
@@ -98,7 +98,7 @@ pub struct GitDiffFile {
     pub extra: String,
     pub additions: usize,
     pub deletions: usize,
-    pub hunks: Vec<GitDiffHunk>
+    pub hunks: Vec<GitDiffHunk>,
 }
 
 #[derive(Serialize, Default)]
@@ -114,8 +114,15 @@ pub struct GitDiffLine {
     pub text: String,
 }
 
-fn walk_file_tree(repo: &git2::Repository, rev: &str, files: &mut Vec<GitFile>,
-                  depth: usize, max_depth: usize, recurse: bool, prefix: &str) -> Result<(), Error> {
+fn walk_file_tree(
+    repo: &git2::Repository,
+    rev: &str,
+    files: &mut Vec<GitFile>,
+    depth: usize,
+    max_depth: usize,
+    recurse: bool,
+    prefix: &str,
+) -> Result<(), Error> {
     let obj = repo.revparse_single(rev)?;
     let tree = obj.peel_to_tree()?;
     for entry in tree.iter() {
@@ -151,23 +158,35 @@ fn walk_file_tree(repo: &git2::Repository, rev: &str, files: &mut Vec<GitFile>,
         });
         if recurse && depth < (max_depth - 1) && entry.kind() == Some(git2::ObjectType::Tree) {
             let prefix = path + "/";
-            walk_file_tree(repo, &entry.id().to_string(), files,
-                           depth+1, max_depth, true, &prefix)?;
+            walk_file_tree(
+                repo,
+                &entry.id().to_string(),
+                files,
+                depth + 1,
+                max_depth,
+                true,
+                &prefix,
+            )?;
         }
     }
     Ok(())
 }
 
 pub fn dir_listing(repo: &Repository, file: &GitFile) -> Result<Vec<GitFile>, Error> {
-    let mut files: Vec<GitFile> = vec!();
+    let mut files: Vec<GitFile> = vec![];
     walk_file_tree(&repo, &file.id, &mut files, 0, usize::MAX, false, "")?;
     Ok(files)
 }
 
-pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, metadata: GitsyMetadata) -> Result<GitRepo, Error> {
-    let mut history: Vec<GitObject> = vec!();
-    let mut branches: Vec<GitObject> = vec!();
-    let mut tags: Vec<GitObject> = vec!();
+pub fn parse_repo(
+    repo: &Repository,
+    name: &str,
+    settings: &GitsySettingsRepo,
+    metadata: GitsyMetadata,
+) -> Result<GitRepo, Error> {
+    let mut history: Vec<GitObject> = vec![];
+    let mut branches: Vec<GitObject> = vec![];
+    let mut tags: Vec<GitObject> = vec![];
     let mut commits: BTreeMap<String, GitObject> = BTreeMap::new();
     let mut commit_count = 0;
     let mut history_count = 0;
@@ -181,8 +200,12 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
         if let (Some(target), Some(name)) = (refr.target(), refr.shorthand()) {
             let id = target.to_string();
             match references.contains_key(&id) {
-                false => { references.insert(target.to_string(), vec!(name.to_string())); },
-                true => { references.get_mut(&id).unwrap().push(name.to_string()); },
+                false => {
+                    references.insert(target.to_string(), vec![name.to_string()]);
+                }
+                true => {
+                    references.get_mut(&id).unwrap().push(name.to_string());
+                }
             }
         }
     }
@@ -194,9 +217,10 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
     loudest!(" - Parsing history:");
     for oid in revwalk {
         let oid = oid?;
-        if commit_count >= settings.limit_commits.unwrap_or(usize::MAX) ||
-            history_count >= settings.limit_history.unwrap_or(usize::MAX) {
-                break;
+        if commit_count >= settings.limit_commits.unwrap_or(usize::MAX)
+            || history_count >= settings.limit_history.unwrap_or(usize::MAX)
+        {
+            break;
         }
         commits.insert(oid.to_string(), parse_commit(repo, &oid.to_string())?);
         commit_count += 1;
@@ -205,7 +229,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
         let full_hash = commit.id().to_string();
         let short_hash = obj.short_id()?.as_str().unwrap_or_default().to_string();
 
-        let mut parents: Vec<String> = vec!();
+        let mut parents: Vec<String> = vec![];
         let a = if commit.parents().len() == 1 {
             let parent = commit.parent(0)?;
             parents.push(parent.id().to_string());
@@ -223,8 +247,10 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
             deletions: stats.deletions(),
         };
 
-        let alt_refs: Vec<String> = references.get(&commit.id().to_string())
-            .map(|x| x.to_owned()).unwrap_or_default();
+        let alt_refs: Vec<String> = references
+            .get(&commit.id().to_string())
+            .map(|x| x.to_owned())
+            .unwrap_or_default();
 
         if history_count < settings.limit_history.unwrap_or(usize::MAX) {
             loudest!("   + {} {}", full_hash, first_line(commit.message_bytes()));
@@ -241,7 +267,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
                 ref_name: None,
                 alt_refs,
                 author: GitAuthor {
-                    name:  commit.author().name().map(|x| x.to_owned()),
+                    name: commit.author().name().map(|x| x.to_owned()),
                     email: commit.author().email().map(|x| x.to_owned()),
                 },
                 summary: Some(first_line(commit.message_bytes())),
@@ -267,7 +293,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
         // this is a bad idea?
         match refr.kind() {
             Some(k) if k == git2::ReferenceType::Symbolic => continue,
-            _ => {},
+            _ => {}
         }
         let commit = repo.find_commit(obj.id())?;
         let full_hash = obj.id().to_string();
@@ -278,7 +304,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
             short_hash,
             ts_utc: commit.author().when().seconds(),
             ts_offset: (commit.author().when().offset_minutes() as i64) * 60,
-            parents: vec!(),
+            parents: vec![],
             ref_name: Some(name.to_string()),
             author: GitAuthor {
                 name: commit.author().name().map(|x| x.to_owned()),
@@ -323,9 +349,8 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
             Some(t) => (t.when().seconds(), (t.when().offset_minutes() as i64) * 60),
             _ => (0, 0),
         };
-        let (author,email) = match commit.tagger() {
-            Some(t) => (t.name().map(|x| x.to_owned()),
-                        t.email().map(|x| x.to_owned())),
+        let (author, email) = match commit.tagger() {
+            Some(t) => (t.name().map(|x| x.to_owned()), t.email().map(|x| x.to_owned())),
             _ => (None, None),
         };
         let summary = match commit.message_bytes() {
@@ -339,10 +364,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
             ts_utc: ts,
             ts_offset: tz,
             ref_name: Some(tag.to_string()),
-            author: GitAuthor {
-                name: author,
-                email,
-            },
+            author: GitAuthor { name: author, email },
             tagged_id: Some(commit.target_id().to_string()),
             message: commit.message().map(|x| x.to_string()),
             summary,
@@ -352,8 +374,8 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
     }
     loud!(" - parsed {} tags", tag_count);
 
-    let mut root_files: Vec<GitFile> = vec!();
-    let mut all_files: Vec<GitFile> = vec!();
+    let mut root_files: Vec<GitFile> = vec![];
+    let mut all_files: Vec<GitFile> = vec![];
     let max_depth = settings.limit_tree_depth.unwrap_or(usize::MAX);
     if max_depth > 0 {
         loudest!(" - Walking root files");
@@ -380,23 +402,21 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
 pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
     let obj = repo.revparse_single(refr)?;
     let commit = repo.find_commit(obj.id())?;
-    let mut parents: Vec<String> = vec!();
+    let mut parents: Vec<String> = vec![];
 
     let a = match commit.parents().len() {
         x if x == 1 => {
             let parent = commit.parent(0).unwrap();
             parents.push(parent.id().to_string());
             Some(parent.tree()?)
-        },
+        }
         x if x > 1 => {
             for parent in commit.parents() {
                 parents.push(parent.id().to_string());
             }
             None
-        },
-        _ => {
-            None
-        },
+        }
+        _ => None,
     };
     let b = commit.tree()?;
     let mut diffopts = DiffOptions::new();
@@ -409,22 +429,38 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
         deletions: stats.deletions(),
         ..Default::default()
     };
-    let files: Rc<RefCell<Vec<GitDiffFile>>> = Rc::new(RefCell::new(vec!()));
+    let files: Rc<RefCell<Vec<GitDiffFile>>> = Rc::new(RefCell::new(vec![]));
 
     diff.foreach(
         &mut |file, _progress| {
             let mut file_diff: GitDiffFile = Default::default();
             file_diff.newfile = match file.status() {
                 git2::Delta::Deleted => "/dev/null".to_owned(),
-                _ => file.new_file().path().map(|x| "b/".to_string() + &x.to_string_lossy()).unwrap_or("/dev/null".to_string()),
+                _ => file
+                    .new_file()
+                    .path()
+                    .map(|x| "b/".to_string() + &x.to_string_lossy())
+                    .unwrap_or("/dev/null".to_string()),
             };
             file_diff.oldfile = match file.status() {
                 git2::Delta::Added => "/dev/null".to_owned(),
-                _ => file.old_file().path().map(|x| "a/".to_string() + &x.to_string_lossy()).unwrap_or("/dev/null".to_string()),
+                _ => file
+                    .old_file()
+                    .path()
+                    .map(|x| "a/".to_string() + &x.to_string_lossy())
+                    .unwrap_or("/dev/null".to_string()),
             };
             file_diff.basefile = match file.status() {
-                git2::Delta::Added => file.new_file().path().map(|x| x.to_string_lossy().to_string()).unwrap_or("/dev/null".to_string()),
-                _ => file.old_file().path().map(|x| x.to_string_lossy().to_string()).unwrap_or("/dev/null".to_string()),
+                git2::Delta::Added => file
+                    .new_file()
+                    .path()
+                    .map(|x| x.to_string_lossy().to_string())
+                    .unwrap_or("/dev/null".to_string()),
+                _ => file
+                    .old_file()
+                    .path()
+                    .map(|x| x.to_string_lossy().to_string())
+                    .unwrap_or("/dev/null".to_string()),
             };
             file_diff.oldid = file.old_file().id().to_string();
             file_diff.newid = file.new_file().id().to_string();
@@ -443,7 +479,10 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
         Some(&mut |_file, _hunk, line| {
             let mut files = files.borrow_mut();
             let file_diff: &mut GitDiffFile = files.last_mut().expect("Diff hunk not associated with a file!");
-            let hunk_diff: &mut GitDiffHunk = file_diff.hunks.last_mut().expect("Diff line not associated with a hunk!");
+            let hunk_diff: &mut GitDiffHunk = file_diff
+                .hunks
+                .last_mut()
+                .expect("Diff line not associated with a hunk!");
             let (kind, prefix) = match line.origin() {
                 ' ' => ("ctx", " "),
                 '-' => ("del", "-"),
@@ -453,7 +492,7 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
             match line.origin() {
                 '-' => file_diff.deletions += 1,
                 '+' => file_diff.additions += 1,
-                _ => {},
+                _ => {}
             }
             let line_diff = GitDiffLine {
                 text: String::from_utf8_lossy(line.content()).to_string(),
@@ -462,15 +501,15 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
             };
             hunk_diff.lines.push(line_diff);
             true
-        })
+        }),
     )?;
 
     match Rc::try_unwrap(files) {
         Ok(files) => {
             let files: Vec<GitDiffFile> = files.into_inner();
             commit_diff.files = files;
-        },
-        Err(_) => {},
+        }
+        Err(_) => {}
     }
 
     let tree = obj.peel_to_tree()?;
@@ -483,7 +522,7 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
         tree_id: Some(tree.id().to_string()),
         parents,
         ref_name: None,
-        alt_refs: vec!(),
+        alt_refs: vec![],
         author: GitAuthor {
             name: commit.author().name().map(|x| x.to_string()),
             email: commit.author().email().map(|x| x.to_string()),

diff --git a/src/settings.rs b/src/settings.rs
line changes: +52/-43
index 6210fe5..09a34c9
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -1,10 +1,6 @@
-use clap::Parser;
 use crate::error;
-use crate::git::{
-    GitFile,
-    GitObject,
-    GitRepo,
-};
+use crate::git::{GitFile, GitObject, GitRepo};
+use clap::Parser;
 use serde::Deserialize;
 use std::collections::{BTreeMap, HashMap, HashSet};
 use std::fs::{create_dir_all, read_dir, read_to_string};
@@ -39,7 +35,10 @@ impl GitsyCli {
     pub fn new() -> Self {
         let cli = CliArgs::parse();
         let config_path = cli.config.as_deref().unwrap_or(Path::new("config.toml")).to_owned();
-        let config_dir = config_path.parent().expect("Config file not in valid directory.").to_owned();
+        let config_dir = config_path
+            .parent()
+            .expect("Config file not in valid directory.")
+            .to_owned();
         let config_dir = match config_dir.to_str().unwrap_or_default().len() > 0 {
             true => config_dir,
             false => PathBuf::from("."),
@@ -52,10 +51,13 @@ impl GitsyCli {
             Ok(d) => d,
             _ => config_dir.clone(),
         };
-        crate::util::VERBOSITY.store(match cli.quiet {
-            true => 0,
-            false => (cli.verbose + 1).into(),
-        }, Ordering::Relaxed);
+        crate::util::VERBOSITY.store(
+            match cli.quiet {
+                true => 0,
+                false => (cli.verbose + 1).into(),
+            },
+            Ordering::Relaxed,
+        );
         GitsyCli {
             path: config_path,
             dir: config_dir,
@@ -63,7 +65,6 @@ impl GitsyCli {
     }
 }
 
-
 #[derive(Deserialize, Debug)]
 pub struct GitsySettingsTemplates {
     pub path: PathBuf,
@@ -102,7 +103,7 @@ macro_rules! output_path_fn {
                 (true, true) => {
                     let name = repo.map(|x| &x.name).unwrap();
                     tmpl_str.replace("%REPO%", name)
-                },
+                }
                 (true, false) => {
                     panic!("%REPO% variable not available for output path: {}", tmpl_str);
                 }
@@ -112,7 +113,7 @@ macro_rules! output_path_fn {
                 (true, true) => {
                     let name = obj.map(|x| &x.$id).unwrap();
                     tmpl_str.replace("%ID%", name)
-                },
+                }
                 (true, false) => {
                     panic!("%ID% variable not available for output path: {}", tmpl_str);
                 }
@@ -124,12 +125,12 @@ macro_rules! output_path_fn {
             match $is_dir {
                 true => {
                     let _ = create_dir_all(&path);
-                },
+                }
                 false => {
                     if let Some(dir) = path.parent() {
                         let _ = create_dir_all(dir);
                     }
-                },
+                }
             }
             path.to_str()
                 .expect(&format!("Output is not a valid path: {}", path.display()))
@@ -140,17 +141,17 @@ macro_rules! output_path_fn {
 //step_map_first!(boil_in_wort, Boil, Wort, |b: &Boil| { b.wort_start() });
 
 impl GitsySettingsOutputs {
-    output_path_fn!(repo_list,     GitObject, full_hash, false, "repos.html");
-    output_path_fn!(repo_summary,  GitObject, full_hash, false, "%REPO%/summary.html");
-    output_path_fn!(commit,        GitObject, full_hash, false, "%REPO%/commit/%ID%.html");
-    output_path_fn!(branch,        GitObject, full_hash, false, "%REPO%/branch/%ID%.html");
-    output_path_fn!(tag,           GitObject, full_hash, false, "%REPO%/tag/%ID%.html");
-    output_path_fn!(file,          GitFile,   id,        false, "%REPO%/file/%ID%.html");
-    output_path_fn!(syntax_css,    GitObject, full_hash, false, "%REPO%/file/syntax.css");
-    output_path_fn!(dir,           GitFile,   id,        false, "%REPO%/dir/%ID%.html");
-    output_path_fn!(error,         GitObject, full_hash, false, "404.html");
-    output_path_fn!(global_assets, GitObject, full_hash, true,  "assets/");
-    output_path_fn!(repo_assets  , GitObject, full_hash, true,  "%REPO%/assets/");
+    output_path_fn!(repo_list, GitObject, full_hash, false, "repos.html");
+    output_path_fn!(repo_summary, GitObject, full_hash, false, "%REPO%/summary.html");
+    output_path_fn!(commit, GitObject, full_hash, false, "%REPO%/commit/%ID%.html");
+    output_path_fn!(branch, GitObject, full_hash, false, "%REPO%/branch/%ID%.html");
+    output_path_fn!(tag, GitObject, full_hash, false, "%REPO%/tag/%ID%.html");
+    output_path_fn!(file, GitFile, id, false, "%REPO%/file/%ID%.html");
+    output_path_fn!(syntax_css, GitObject, full_hash, false, "%REPO%/file/syntax.css");
+    output_path_fn!(dir, GitFile, id, false, "%REPO%/dir/%ID%.html");
+    output_path_fn!(error, GitObject, full_hash, false, "404.html");
+    output_path_fn!(global_assets, GitObject, full_hash, true, "assets/");
+    output_path_fn!(repo_assets, GitObject, full_hash, true, "%REPO%/assets/");
 }
 
 #[derive(Clone, Deserialize, Default, Debug)]
@@ -222,22 +223,26 @@ impl GitsySettings {
         let settings: GitsySettings = toml::from_str(&toml).expect("Configuration file is invalid.");
 
         // Settings are valid, so let's move into the directory with the config file
-        if cli.dir.to_str().unwrap_or_default().len() > 0 { // empty string means current directory
+        if cli.dir.to_str().unwrap_or_default().len() > 0 {
+            // empty string means current directory
             std::env::set_current_dir(&cli.dir)
                 .expect(&format!("Unable to set working directory to: {}", cli.dir.display()));
         }
 
         // Get a list of all remaining TOML "tables" in the file.
         // These are the user-supplied individual repositories.
-        let reserved_keys = vec!("gitsy_templates", "gitsy_outputs", "gitsy_extra");
+        let reserved_keys = vec!["gitsy_templates", "gitsy_outputs", "gitsy_extra"];
         let settings_raw: HashMap<String, toml::Value> = toml::from_str(&toml).expect("blah");
-        let table_keys: Vec<String> = settings_raw.iter().filter_map(|x| match x.1.is_table() {
-            true => match reserved_keys.contains(&x.0.as_str()) {
-                false => Some(x.0.clone()),
-                true => None,
-            },
-            false => None
-        }).collect();
+        let table_keys: Vec<String> = settings_raw
+            .iter()
+            .filter_map(|x| match x.1.is_table() {
+                true => match reserved_keys.contains(&x.0.as_str()) {
+                    false => Some(x.0.clone()),
+                    true => None,
+                },
+                false => None,
+            })
+            .collect();
 
         // Try to convert each unknown "table" into a repo struct, and
         // save the ones that are successful.  If no repo name is
@@ -245,14 +250,18 @@ impl GitsySettings {
         let mut repo_descriptions: HashSet<GitsySettingsRepo> = HashSet::new();
         macro_rules! global_to_repo {
             ($settings:ident, $repo:ident, $field:ident) => {
-                if $repo.$field.is_none() { $repo.$field = $settings.$field.clone() }
-            }
+                if $repo.$field.is_none() {
+                    $repo.$field = $settings.$field.clone()
+                }
+            };
         }
         for k in &table_keys {
             let v = settings_raw.get(k).unwrap();
             match toml::from_str::<GitsySettingsRepo>(&v.to_string()) {
                 Ok(mut repo) => {
-                    if repo.name.is_none() { repo.name = Some(k.clone()); }
+                    if repo.name.is_none() {
+                        repo.name = Some(k.clone());
+                    }
                     global_to_repo!(settings, repo, render_markdown);
                     global_to_repo!(settings, repo, syntax_highlight);
                     global_to_repo!(settings, repo, syntax_highlight_theme);
@@ -266,10 +275,10 @@ impl GitsySettings {
                     global_to_repo!(settings, repo, limit_total_size);
 
                     repo_descriptions.insert(repo);
-                },
+                }
                 Err(e) => {
                     error!("Failed to parse repo [{}]: {:?}", k, e);
-                },
+                }
             }
         }
 
@@ -297,8 +306,8 @@ impl GitsySettings {
                         });
                     }
                 }
-            },
-            _ => {},
+            }
+            _ => {}
         }
         (settings, repo_descriptions)
     }

diff --git a/src/template.rs b/src/template.rs
line changes: +22/-20
index 611358e..48efbb4
--- a/src/template.rs
+++ b/src/template.rs
@@ -1,16 +1,13 @@
-use chrono::{
-    DateTime,
-    offset::FixedOffset,
-    naive::NaiveDateTime,
-};
 use crate::git::GitFile;
+use chrono::{naive::NaiveDateTime, offset::FixedOffset, DateTime};
 use std::collections::HashMap;
-use tera::{Filter, Function, Value, to_value, try_get_value};
+use tera::{to_value, try_get_value, Filter, Function, Value};
 
 fn ts_to_date(ts: i64, offset: Option<i64>, format: Option<String>) -> String {
     let offset = offset.unwrap_or(0);
     let dt = NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("Invalid timestamp");
-    let dt_tz: DateTime<FixedOffset> = DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
+    let dt_tz: DateTime<FixedOffset> =
+        DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
     match format {
         Some(f) => dt_tz.format(&f).to_string(),
         None => dt_tz.format("%Y-%m-%d").to_string(),
@@ -20,32 +17,37 @@ fn ts_to_date(ts: i64, offset: Option<i64>, format: Option<String>) -> String {
 fn ts_to_git_timestamp(ts: i64, offset: Option<i64>) -> String {
     let offset = offset.unwrap_or(0);
     let dt = chrono::naive::NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("invalid timestamp");
-    let dt_tz: DateTime<FixedOffset> = DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
+    let dt_tz: DateTime<FixedOffset> =
+        DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
     dt_tz.format("%a %b %e %T %Y %z").to_string()
 }
 
 pub struct FileFilter;
 impl Filter for FileFilter {
-    fn filter(&self, value: &Value, _args: &HashMap<String, Value>
-    ) -> Result<Value, tera::Error> {
+    fn filter(&self, value: &Value, _args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
         let file_list: Vec<GitFile> = try_get_value!("only_files", "value", Vec<GitFile>, value);
-        let file_list: Vec<GitFile> = file_list.iter().filter_map(|x| match x.kind.as_str() {
-            "file" => Some(x.clone()),
-            _ => None,
-        }).collect();
+        let file_list: Vec<GitFile> = file_list
+            .iter()
+            .filter_map(|x| match x.kind.as_str() {
+                "file" => Some(x.clone()),
+                _ => None,
+            })
+            .collect();
         Ok(to_value(file_list).unwrap())
     }
 }
 
 pub struct DirFilter;
 impl Filter for DirFilter {
-    fn filter(&self, value: &Value, _args: &HashMap<String, Value>
-    ) -> Result<Value, tera::Error> {
+    fn filter(&self, value: &Value, _args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
         let file_list: Vec<GitFile> = try_get_value!("only_dirs", "value", Vec<GitFile>, value);
-        let file_list: Vec<GitFile> = file_list.iter().filter_map(|x| match x.kind.as_str() {
-            "dir" => Some(x.clone()),
-            _ => None,
-        }).collect();
+        let file_list: Vec<GitFile> = file_list
+            .iter()
+            .filter_map(|x| match x.kind.as_str() {
+                "dir" => Some(x.clone()),
+                _ => None,
+            })
+            .collect();
         Ok(to_value(file_list).unwrap())
     }
 }