summary history branches tags files
commit:0966bb534f5997a62dc3b9f3f6ca0b5bc1931c5f
author:Trevor Bentley
committer:Trevor Bentley
date:Fri Jan 13 04:37:40 2023 +0100
parents:51007c68b894a9adce76bd3016b005f324d57594
add a files output template
diff --git a/config.toml b/config.toml
line changes: +10/-1
index bc59d03..68c111e
--- a/config.toml
+++ b/config.toml
@@ -115,7 +115,7 @@ syntax_highlight = true
 # - `base16-ocean.dark`
 # - `base16-eighties.dark`
 # - `base16-mocha.dark`
-# - `InspiredGithub`
+# - `InspiredGitHub`
 # - `Solarized (dark)`
 # - `Solarized (light)`
 #
@@ -317,6 +317,14 @@ tags         = "tags.html"
 # This template executes many times.
 tag          = "tag.html"
 
+# Template responsible for displaying file tree.
+#
+# Called with the same variables as the `summary` page, this simply gives an
+# alternative locate to list the files in the root of the repository.
+#
+# This template executes once per repository.
+files        = "files.html"
+
 # Template responsible for displaying a single file.
 #
 # Called once per parsed file, with both the whole repository and the current
@@ -373,6 +381,7 @@ branches      = "%REPO%/branches%PAGE%.html"
 branch        = "%REPO%/branch/%ID%.html"
 tags          = "%REPO%/tags%PAGE%.html"
 tag           = "%REPO%/tag/%ID%.html"
+files         = "%REPO%/files.html"
 file          = "%REPO%/file/%ID%.html"
 syntax_css    = "%REPO%/file/syntax.css"
 dir           = "%REPO%/dir/%ID%.html"

diff --git a/src/generate.rs b/src/generate.rs
line changes: +23/-2
index 04a1e2b..aa12ff7
--- a/src/generate.rs
+++ b/src/generate.rs
@@ -3,7 +3,7 @@ use crate::{
     git::{dir_listing, parse_repo, GitFile, GitRepo, GitsyMetadata},
     loud, louder, loudest, normal, normal_noln,
     settings::{GitsyCli, GitsyRepoDescriptions, GitsySettings, GitsySettingsRepo},
-    template::{DirFilter, FileFilter, Pagination, TsDateFn, TsTimestampFn},
+    template::{DirFilter, FileFilter, HexFilter, MaskFilter, OctFilter, Pagination, TsDateFn, TsTimestampFn},
     util::GitsyError,
 };
 use git2::{Error, Repository};
@@ -169,6 +169,9 @@ impl GitsyGenerator {
         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_filter("hex", HexFilter {});
+        tera.register_filter("oct", OctFilter {});
+        tera.register_filter("mask", MaskFilter {});
         tera.register_function("ts_to_date", TsDateFn {});
         tera.register_function("ts_to_git_timestamp", TsTimestampFn {});
         Ok(tera)
@@ -330,6 +333,8 @@ impl GitsyGenerator {
             if let Some(site_description) = &self.settings.site_description {
                 local_ctx.insert("site_description", site_description);
             }
+            local_ctx.insert("site_dir", &self.settings.outputs.output_dir());
+            local_ctx.insert("site_assets", &self.settings.outputs.global_assets(None, None));
             local_ctx.insert("site_generated_ts", &generated_dt.timestamp());
             local_ctx.insert("site_generated_offset", &generated_dt.offset().local_minus_utc());
 
@@ -488,7 +493,7 @@ impl GitsyGenerator {
                         repo_desc
                             .syntax_highlight_theme
                             .as_deref()
-                            .unwrap_or("base16-ocean.light"),
+                            .unwrap_or("base16-ocean.dark"),
                     )
                     .expect("Invalid syntax highlighting theme specified.");
                 let css: String = css_for_theme_with_class_style(theme, syntect::html::ClassStyle::Spaced)
@@ -549,6 +554,7 @@ impl GitsyGenerator {
                     continue;
                 }
                 let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
+                local_ctx.insert("dir", dir);
                 local_ctx
                     .try_insert("files", &listing)
                     .expect("Failed to add dir to template engine.");
@@ -564,6 +570,19 @@ impl GitsyGenerator {
                     }
                 }
                 local_ctx.remove("files");
+                local_ctx.remove("dir");
+            }
+
+            if let Some(templ_file) = self.settings.templates.files.as_deref() {
+                match tera.render(templ_file, &local_ctx) {
+                    Ok(rendered) => {
+                        repo_bytes +=
+                            self.write_rendered(&self.settings.outputs.files(Some(&summary), None), &rendered);
+                    }
+                    Err(x) => match x.kind {
+                        _ => error!("ERROR: {:?}", x),
+                    },
+                }
             }
 
             if repo_desc.asset_files.is_some() {
@@ -625,6 +644,8 @@ impl GitsyGenerator {
         if let Some(site_description) = &self.settings.site_description {
             global_ctx.insert("site_description", site_description);
         }
+        global_ctx.insert("site_dir", &self.settings.outputs.output_dir());
+        global_ctx.insert("site_assets", &self.settings.outputs.global_assets(None, None));
         global_ctx.insert("site_generated_ts", &generated_dt.timestamp());
         global_ctx.insert("site_generated_offset", &generated_dt.offset().local_minus_utc());
 

diff --git a/src/main.rs b/src/main.rs
line changes: +1/-0
index 7f2659e..984fcb6
--- a/src/main.rs
+++ b/src/main.rs
@@ -10,6 +10,7 @@ use settings::{GitsyCli, GitsySettings};
 // TODO:
 //
 //   * basic, light, dark, and fancy default themes
+//   * specify default branch, use instead of HEAD
 //   * better error propagation
 //   * automated tests
 //   * documentation + examples

diff --git a/src/settings.rs b/src/settings.rs
line changes: +10/-0
index 9c11833..4e873ff
--- a/src/settings.rs
+++ b/src/settings.rs
@@ -76,6 +76,7 @@ pub struct GitsySettingsTemplates {
     pub branch: Option<String>,
     pub tags: Option<String>,
     pub tag: Option<String>,
+    pub files: Option<String>,
     pub file: Option<String>,
     pub dir: Option<String>,
     pub error: Option<String>,
@@ -93,6 +94,7 @@ pub struct GitsySettingsOutputs {
     pub branch: Option<String>,
     pub tags: Option<String>,
     pub tag: Option<String>,
+    pub files: Option<String>,
     pub file: Option<String>,
     pub dir: Option<String>,
     pub error: Option<String>,
@@ -159,12 +161,20 @@ impl GitsySettingsOutputs {
     output_path_fn!(branch,          GitObject, full_hash, false, "%REPO%/branch/%ID%.html");
     output_path_fn!(tags,            GitObject, full_hash, false, "%REPO%/tags%PAGE%.html");
     output_path_fn!(tag,             GitObject, full_hash, false, "%REPO%/tag/%ID%.html");
+    output_path_fn!(files,           GitObject, full_hash, false, "%REPO%/files.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/");
+
+    pub fn output_dir(&self) -> String {
+        self.path.clone().canonicalize()
+            .expect(&format!("ERROR: unable to canonicalize output path: {}", self.path.display()))
+            .to_str().expect(&format!("ERROR: unable to parse output path: {}", self.path.display()))
+            .to_string()
+    }
 }
 
 #[derive(Clone, Deserialize, Default, Debug)]

diff --git a/src/template.rs b/src/template.rs
line changes: +41/-1
index 64d0232..5b59865
--- a/src/template.rs
+++ b/src/template.rs
@@ -3,7 +3,7 @@ use chrono::{naive::NaiveDateTime, offset::FixedOffset, DateTime};
 use serde::Serialize;
 use std::collections::HashMap;
 use std::path::PathBuf;
-use tera::{to_value, try_get_value, Filter, Function, Value};
+use tera::{from_value, 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);
@@ -54,6 +54,46 @@ impl Filter for DirFilter {
     }
 }
 
+pub struct HexFilter;
+impl Filter for HexFilter {
+    fn filter(&self, value: &Value, _args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
+        let v: i64 = try_get_value!("hex", "value", i64, value);
+        Ok(to_value(format!("{:x}", v)).unwrap())
+    }
+}
+
+pub struct OctFilter;
+impl Filter for OctFilter {
+    fn filter(&self, value: &Value, _args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
+        let v: i64 = try_get_value!("oct", "value", i64, value);
+        Ok(to_value(format!("{:o}", v)).unwrap())
+    }
+}
+
+pub struct MaskFilter;
+impl Filter for MaskFilter {
+    fn filter(&self, value: &Value, args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
+        let v: u64 = try_get_value!("mask", "value", u64, value);
+        let mask: String = from_value(
+            args.get("mask")
+                .expect("ERROR: Tera mask filter called without `mask` parameter.")
+                .clone(),
+        )
+            .expect("ERROR: Tera `mask` parameter is not valid.");
+        let mask: u64 = match mask.starts_with("0x") {
+            true => {
+                let hexstr = mask.strip_prefix("0x").unwrap();
+                u64::from_str_radix(hexstr, 16)
+                    .expect("ERROR: Tera `mask` parameter is invalid hex.")
+            },
+            false => {
+                str::parse::<u64>(&mask).expect("ERROR: Tera `mask` parameter is not valid.")
+            },
+        };
+        Ok(to_value(v & mask).unwrap())
+    }
+}
+
 pub struct TsDateFn;
 impl Function for TsDateFn {
     fn call(&self, args: &HashMap<String, Value>) -> Result<Value, tera::Error> {