add a files output template
This commit is contained in:
+10
-1
@@ -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
@@ -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());
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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> {
|
||||||
|
|||||||
Reference in New Issue
Block a user