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};
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)
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());
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)
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.");
}
}
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 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());
// TODO:
//
// * basic, light, dark, and fancy default themes
+// * specify default branch, use instead of HEAD
// * better error propagation
// * automated tests
// * documentation + examples
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>,
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>,
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)]
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);
}
}
+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> {