add a files output template

This commit is contained in:
Trevor Bentley
2023-01-13 04:37:40 +01:00
parent 51007c68b8
commit 0966bb534f
5 changed files with 85 additions and 4 deletions
+10 -1
View File
@@ -115,7 +115,7 @@ syntax_highlight = true
# - `base16-ocean.dark` # - `base16-ocean.dark`
# - `base16-eighties.dark` # - `base16-eighties.dark`
# - `base16-mocha.dark` # - `base16-mocha.dark`
# - `InspiredGithub` # - `InspiredGitHub`
# - `Solarized (dark)` # - `Solarized (dark)`
# - `Solarized (light)` # - `Solarized (light)`
# #
@@ -317,6 +317,14 @@ tags = "tags.html"
# This template executes many times. # This template executes many times.
tag = "tag.html" 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. # Template responsible for displaying a single file.
# #
# Called once per parsed file, with both the whole repository and the current # 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" branch = "%REPO%/branch/%ID%.html"
tags = "%REPO%/tags%PAGE%.html" tags = "%REPO%/tags%PAGE%.html"
tag = "%REPO%/tag/%ID%.html" tag = "%REPO%/tag/%ID%.html"
files = "%REPO%/files.html"
file = "%REPO%/file/%ID%.html" file = "%REPO%/file/%ID%.html"
syntax_css = "%REPO%/file/syntax.css" syntax_css = "%REPO%/file/syntax.css"
dir = "%REPO%/dir/%ID%.html" dir = "%REPO%/dir/%ID%.html"
+23 -2
View File
@@ -3,7 +3,7 @@ use crate::{
git::{dir_listing, parse_repo, GitFile, GitRepo, GitsyMetadata}, git::{dir_listing, parse_repo, GitFile, GitRepo, GitsyMetadata},
loud, louder, loudest, normal, normal_noln, loud, louder, loudest, normal, normal_noln,
settings::{GitsyCli, GitsyRepoDescriptions, GitsySettings, GitsySettingsRepo}, settings::{GitsyCli, GitsyRepoDescriptions, GitsySettings, GitsySettingsRepo},
template::{DirFilter, FileFilter, Pagination, TsDateFn, TsTimestampFn}, template::{DirFilter, FileFilter, HexFilter, MaskFilter, OctFilter, Pagination, TsDateFn, TsTimestampFn},
util::GitsyError, util::GitsyError,
}; };
use git2::{Error, Repository}; use git2::{Error, Repository};
@@ -169,6 +169,9 @@ impl GitsyGenerator {
let mut tera = Tera::new(&template_path.to_string_lossy().to_string())?; let mut tera = Tera::new(&template_path.to_string_lossy().to_string())?;
tera.register_filter("only_files", FileFilter {}); tera.register_filter("only_files", FileFilter {});
tera.register_filter("only_dirs", DirFilter {}); 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_date", TsDateFn {});
tera.register_function("ts_to_git_timestamp", TsTimestampFn {}); tera.register_function("ts_to_git_timestamp", TsTimestampFn {});
Ok(tera) Ok(tera)
@@ -330,6 +333,8 @@ impl GitsyGenerator {
if let Some(site_description) = &self.settings.site_description { if let Some(site_description) = &self.settings.site_description {
local_ctx.insert("site_description", 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_ts", &generated_dt.timestamp());
local_ctx.insert("site_generated_offset", &generated_dt.offset().local_minus_utc()); local_ctx.insert("site_generated_offset", &generated_dt.offset().local_minus_utc());
@@ -488,7 +493,7 @@ impl GitsyGenerator {
repo_desc repo_desc
.syntax_highlight_theme .syntax_highlight_theme
.as_deref() .as_deref()
.unwrap_or("base16-ocean.light"), .unwrap_or("base16-ocean.dark"),
) )
.expect("Invalid syntax highlighting theme specified."); .expect("Invalid syntax highlighting theme specified.");
let css: String = css_for_theme_with_class_style(theme, syntect::html::ClassStyle::Spaced) let css: String = css_for_theme_with_class_style(theme, syntect::html::ClassStyle::Spaced)
@@ -549,6 +554,7 @@ impl GitsyGenerator {
continue; continue;
} }
let listing = dir_listing(&repo, &dir).expect("Failed to parse file."); let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
local_ctx.insert("dir", dir);
local_ctx local_ctx
.try_insert("files", &listing) .try_insert("files", &listing)
.expect("Failed to add dir to template engine."); .expect("Failed to add dir to template engine.");
@@ -564,6 +570,19 @@ impl GitsyGenerator {
} }
} }
local_ctx.remove("files"); 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() { if repo_desc.asset_files.is_some() {
@@ -625,6 +644,8 @@ impl GitsyGenerator {
if let Some(site_description) = &self.settings.site_description { if let Some(site_description) = &self.settings.site_description {
global_ctx.insert("site_description", 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_ts", &generated_dt.timestamp());
global_ctx.insert("site_generated_offset", &generated_dt.offset().local_minus_utc()); global_ctx.insert("site_generated_offset", &generated_dt.offset().local_minus_utc());
+1
View File
@@ -10,6 +10,7 @@ use settings::{GitsyCli, GitsySettings};
// TODO: // TODO:
// //
// * basic, light, dark, and fancy default themes // * basic, light, dark, and fancy default themes
// * specify default branch, use instead of HEAD
// * better error propagation // * better error propagation
// * automated tests // * automated tests
// * documentation + examples // * documentation + examples
+10
View File
@@ -76,6 +76,7 @@ pub struct GitsySettingsTemplates {
pub branch: Option<String>, pub branch: Option<String>,
pub tags: Option<String>, pub tags: Option<String>,
pub tag: Option<String>, pub tag: Option<String>,
pub files: Option<String>,
pub file: Option<String>, pub file: Option<String>,
pub dir: Option<String>, pub dir: Option<String>,
pub error: Option<String>, pub error: Option<String>,
@@ -93,6 +94,7 @@ pub struct GitsySettingsOutputs {
pub branch: Option<String>, pub branch: Option<String>,
pub tags: Option<String>, pub tags: Option<String>,
pub tag: Option<String>, pub tag: Option<String>,
pub files: Option<String>,
pub file: Option<String>, pub file: Option<String>,
pub dir: Option<String>, pub dir: Option<String>,
pub error: 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!(branch, GitObject, full_hash, false, "%REPO%/branch/%ID%.html");
output_path_fn!(tags, GitObject, full_hash, false, "%REPO%/tags%PAGE%.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!(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!(file, GitFile, id, false, "%REPO%/file/%ID%.html");
output_path_fn!(syntax_css, GitObject, full_hash, false, "%REPO%/file/syntax.css"); 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!(dir, GitFile, id, false, "%REPO%/dir/%ID%.html");
output_path_fn!(error, GitObject, full_hash, false, "404.html"); output_path_fn!(error, GitObject, full_hash, false, "404.html");
output_path_fn!(global_assets, GitObject, full_hash, true, "assets/"); 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_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)] #[derive(Clone, Deserialize, Default, Debug)]
+41 -1
View File
@@ -3,7 +3,7 @@ use chrono::{naive::NaiveDateTime, offset::FixedOffset, DateTime};
use serde::Serialize; use serde::Serialize;
use std::collections::HashMap; use std::collections::HashMap;
use std::path::PathBuf; 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 { fn ts_to_date(ts: i64, offset: Option<i64>, format: Option<String>) -> String {
let offset = offset.unwrap_or(0); 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; pub struct TsDateFn;
impl Function for TsDateFn { impl Function for TsDateFn {
fn call(&self, args: &HashMap<String, Value>) -> Result<Value, tera::Error> { fn call(&self, args: &HashMap<String, Value>) -> Result<Value, tera::Error> {