switch from crazy macro to trait for path variable substitution
This commit is contained in:
+16
-14
@@ -145,7 +145,9 @@ impl GitsyGenerator {
|
|||||||
Ok(file)
|
Ok(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_rendered(&self, path: &str, rendered: &str) -> usize {
|
fn write_rendered<P: AsRef<Path>>(&self, path: &P, rendered: &str) -> usize {
|
||||||
|
let path = path.as_ref().to_str()
|
||||||
|
.expect(&format!("ERROR: attempted to write unrecognizeable path: {}", path.as_ref().display()));
|
||||||
// Ensure that the requested output path is actually a child
|
// Ensure that the requested output path is actually a child
|
||||||
// of the output directory, as a sanity check to ensure we
|
// of the output directory, as a sanity check to ensure we
|
||||||
// aren't writing out of bounds.
|
// aren't writing out of bounds.
|
||||||
@@ -364,7 +366,7 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
local_ctx.insert("site_dir", &self.settings.outputs.output_dir());
|
local_ctx.insert("site_dir", &self.settings.outputs.output_dir());
|
||||||
if self.settings.outputs.global_assets.is_some() {
|
if self.settings.outputs.global_assets.is_some() {
|
||||||
local_ctx.insert("site_assets", &self.settings.outputs.to_relative(&self.settings.outputs.global_assets(None, None)));
|
local_ctx.insert("site_assets", &self.settings.outputs.to_relative(&self.settings.outputs.global_assets::<GitFile>(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());
|
||||||
@@ -385,7 +387,7 @@ impl GitsyGenerator {
|
|||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes +=
|
repo_bytes +=
|
||||||
self.write_rendered(&self.settings.outputs.summary(Some(&parsed_repo), None), &rendered);
|
self.write_rendered(&self.settings.outputs.summary::<GitFile>(Some(&parsed_repo), None), &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -402,7 +404,7 @@ impl GitsyGenerator {
|
|||||||
let pagination = Pagination::new(
|
let pagination = Pagination::new(
|
||||||
idx + 1,
|
idx + 1,
|
||||||
page_count,
|
page_count,
|
||||||
&self.settings.outputs.branches(Some(&parsed_repo), None),
|
&self.settings.outputs.branches::<GitFile>(Some(&parsed_repo), None),
|
||||||
);
|
);
|
||||||
paged_ctx.insert("page", &pagination.with_relative_paths());
|
paged_ctx.insert("page", &pagination.with_relative_paths());
|
||||||
paged_ctx.insert("branches", &page);
|
paged_ctx.insert("branches", &page);
|
||||||
@@ -443,7 +445,7 @@ impl GitsyGenerator {
|
|||||||
let page_count = pages.len();
|
let page_count = pages.len();
|
||||||
for (idx, page) in pages.enumerate() {
|
for (idx, page) in pages.enumerate() {
|
||||||
let pagination =
|
let pagination =
|
||||||
Pagination::new(idx + 1, page_count, &self.settings.outputs.tags(Some(&parsed_repo), None));
|
Pagination::new(idx + 1, page_count, &self.settings.outputs.tags::<GitFile>(Some(&parsed_repo), None));
|
||||||
paged_ctx.insert("page", &pagination.with_relative_paths());
|
paged_ctx.insert("page", &pagination.with_relative_paths());
|
||||||
paged_ctx.insert("tags", &page);
|
paged_ctx.insert("tags", &page);
|
||||||
match tera.render(templ_file, &paged_ctx) {
|
match tera.render(templ_file, &paged_ctx) {
|
||||||
@@ -491,7 +493,7 @@ impl GitsyGenerator {
|
|||||||
let pagination = Pagination::new(
|
let pagination = Pagination::new(
|
||||||
idx + 1,
|
idx + 1,
|
||||||
page_count,
|
page_count,
|
||||||
&self.settings.outputs.history(Some(&parsed_repo), None),
|
&self.settings.outputs.history::<GitFile>(Some(&parsed_repo), None),
|
||||||
);
|
);
|
||||||
paged_ctx.insert("page", &pagination.with_relative_paths());
|
paged_ctx.insert("page", &pagination.with_relative_paths());
|
||||||
paged_ctx.insert("history", &page);
|
paged_ctx.insert("history", &page);
|
||||||
@@ -542,7 +544,7 @@ impl GitsyGenerator {
|
|||||||
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)
|
||||||
.expect("Invalid syntax highlighting theme specified.");
|
.expect("Invalid syntax highlighting theme specified.");
|
||||||
repo_bytes +=
|
repo_bytes +=
|
||||||
self.write_rendered(&self.settings.outputs.syntax_css(Some(&parsed_repo), None), css.as_str());
|
self.write_rendered(&self.settings.outputs.syntax_css::<GitFile>(Some(&parsed_repo), None), css.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: parallelize the rest of the processing steps. This one is
|
// TODO: parallelize the rest of the processing steps. This one is
|
||||||
@@ -620,7 +622,7 @@ impl GitsyGenerator {
|
|||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes +=
|
repo_bytes +=
|
||||||
self.write_rendered(&self.settings.outputs.files(Some(&parsed_repo), None), &rendered);
|
self.write_rendered(&self.settings.outputs.files::<GitFile>(Some(&parsed_repo), None), &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -629,7 +631,7 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if repo_desc.asset_files.is_some() {
|
if repo_desc.asset_files.is_some() {
|
||||||
let target_dir = self.settings.outputs.repo_assets(Some(&parsed_repo), None);
|
let target_dir = self.settings.outputs.repo_assets::<GitFile>(Some(&parsed_repo), None);
|
||||||
for src_file in repo_desc.asset_files.as_ref().unwrap() {
|
for src_file in repo_desc.asset_files.as_ref().unwrap() {
|
||||||
let src_file = src_file.replace("%TEMPLATE%", &self.settings.templates.template_dir());
|
let src_file = src_file.replace("%TEMPLATE%", &self.settings.templates.template_dir());
|
||||||
let src_file = src_file.replace("%REPO%", &repo_path);
|
let src_file = src_file.replace("%REPO%", &repo_path);
|
||||||
@@ -692,7 +694,7 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
global_ctx.insert("site_dir", &self.settings.outputs.output_dir());
|
global_ctx.insert("site_dir", &self.settings.outputs.output_dir());
|
||||||
if self.settings.outputs.global_assets.is_some() {
|
if self.settings.outputs.global_assets.is_some() {
|
||||||
global_ctx.insert("site_assets", &self.settings.outputs.to_relative(&self.settings.outputs.global_assets(None, None)));
|
global_ctx.insert("site_assets", &self.settings.outputs.to_relative(&self.settings.outputs.global_assets::<GitFile>(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());
|
||||||
@@ -700,7 +702,7 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.repo_list.as_deref() {
|
if let Some(templ_file) = self.settings.templates.repo_list.as_deref() {
|
||||||
match tera.render(templ_file, &global_ctx) {
|
match tera.render(templ_file, &global_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes += self.write_rendered(&self.settings.outputs.repo_list(None, None), &rendered);
|
global_bytes += self.write_rendered(&self.settings.outputs.repo_list::<GitFile>(None, None), &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -711,7 +713,7 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.error.as_deref() {
|
if let Some(templ_file) = self.settings.templates.error.as_deref() {
|
||||||
match tera.render(templ_file, &global_ctx) {
|
match tera.render(templ_file, &global_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes += self.write_rendered(&self.settings.outputs.error(None, None), &rendered);
|
global_bytes += self.write_rendered(&self.settings.outputs.error::<GitFile>(None, None), &rendered);
|
||||||
}
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
@@ -720,7 +722,7 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.settings.asset_files.is_some() {
|
if self.settings.asset_files.is_some() {
|
||||||
let target_dir = self.settings.outputs.global_assets(None, None);
|
let target_dir = self.settings.outputs.global_assets::<GitFile>(None, None);
|
||||||
for src_file in self.settings.asset_files.as_ref().unwrap() {
|
for src_file in self.settings.asset_files.as_ref().unwrap() {
|
||||||
let src_file = src_file.replace("%TEMPLATE%", &self.settings.templates.template_dir());
|
let src_file = src_file.replace("%TEMPLATE%", &self.settings.templates.template_dir());
|
||||||
let src_file = PathBuf::from(src_file);
|
let src_file = PathBuf::from(src_file);
|
||||||
@@ -752,7 +754,7 @@ impl GitsyGenerator {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if self.cli.should_open {
|
if self.cli.should_open {
|
||||||
let _ = open::that(&format!("file://{}", self.settings.outputs.repo_list(None, None)));
|
let _ = open::that(&format!("file://{}", self.settings.outputs.repo_list::<GitFile>(None, None).display()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
+57
@@ -21,11 +21,13 @@
|
|||||||
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
|
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::settings::GitsySettingsRepo;
|
use crate::settings::GitsySettingsRepo;
|
||||||
|
use crate::util::{sanitize_path_component, SafePathVar, urlify_path};
|
||||||
use crate::{error, loud, loudest};
|
use crate::{error, loud, loudest};
|
||||||
use git2::{DiffOptions, Error, Repository};
|
use git2::{DiffOptions, Error, Repository};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
@@ -86,6 +88,21 @@ impl GitRepo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SafePathVar for GitRepo {
|
||||||
|
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf {
|
||||||
|
let src: &Path = path.as_ref();
|
||||||
|
let mut dst = PathBuf::new();
|
||||||
|
let safe_name = sanitize_path_component(&self.name);
|
||||||
|
for cmp in src.components() {
|
||||||
|
let cmp = cmp.as_os_str().to_string_lossy().replace("%REPO%", &safe_name);
|
||||||
|
dst.push(cmp);
|
||||||
|
}
|
||||||
|
assert!(src.components().count() == dst.components().count(),
|
||||||
|
"ERROR: path substitution accidentally created a new folder in: {}", src.display());
|
||||||
|
dst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Default)]
|
#[derive(Clone, Serialize, Default)]
|
||||||
pub struct GitsyMetadata {
|
pub struct GitsyMetadata {
|
||||||
pub full_name: Option<String>,
|
pub full_name: Option<String>,
|
||||||
@@ -120,6 +137,26 @@ pub struct GitObject {
|
|||||||
pub diff: Option<GitDiffCommit>,
|
pub diff: Option<GitDiffCommit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SafePathVar for GitObject {
|
||||||
|
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf {
|
||||||
|
let src: &Path = path.as_ref();
|
||||||
|
let mut dst = PathBuf::new();
|
||||||
|
let safe_full_hash = sanitize_path_component(&self.full_hash);
|
||||||
|
let safe_ref = self.ref_name.as_deref()
|
||||||
|
.map(|v| sanitize_path_component(&v))
|
||||||
|
.unwrap_or("%REF%".to_string());
|
||||||
|
for cmp in src.components() {
|
||||||
|
let cmp = cmp.as_os_str().to_string_lossy()
|
||||||
|
.replace("%ID%", &safe_full_hash)
|
||||||
|
.replace("%REF%", &safe_ref);
|
||||||
|
dst.push(cmp);
|
||||||
|
}
|
||||||
|
assert!(src.components().count() == dst.components().count(),
|
||||||
|
"ERROR: path substitution accidentally created a new folder in: {}", src.display());
|
||||||
|
dst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Default)]
|
#[derive(Clone, Serialize, Default)]
|
||||||
pub struct GitStats {
|
pub struct GitStats {
|
||||||
pub files: usize,
|
pub files: usize,
|
||||||
@@ -142,6 +179,26 @@ pub struct GitFile {
|
|||||||
pub contents_preformatted: bool,
|
pub contents_preformatted: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl SafePathVar for GitFile {
|
||||||
|
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf {
|
||||||
|
let src: &Path = path.as_ref();
|
||||||
|
let mut dst = PathBuf::new();
|
||||||
|
let safe_id = sanitize_path_component(&self.id);
|
||||||
|
let safe_name = sanitize_path_component(&self.name);
|
||||||
|
let safe_path = sanitize_path_component(&urlify_path(&self.path));
|
||||||
|
for cmp in src.components() {
|
||||||
|
let cmp = cmp.as_os_str().to_string_lossy()
|
||||||
|
.replace("%ID%", &safe_id)
|
||||||
|
.replace("%NAME%", &safe_name)
|
||||||
|
.replace("%PATH%", &safe_path);
|
||||||
|
dst.push(cmp);
|
||||||
|
}
|
||||||
|
assert!(src.components().count() == dst.components().count(),
|
||||||
|
"ERROR: path substitution accidentally created a new folder in: {}", src.display());
|
||||||
|
dst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Default)]
|
#[derive(Clone, Serialize, Default)]
|
||||||
pub struct GitDiffCommit {
|
pub struct GitDiffCommit {
|
||||||
pub files: Vec<GitDiffFile>,
|
pub files: Vec<GitDiffFile>,
|
||||||
|
|||||||
+79
-61
@@ -21,7 +21,8 @@
|
|||||||
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
|
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::git::{GitFile, GitObject, GitRepo};
|
use crate::git::GitRepo;
|
||||||
|
use crate::util::SafePathVar;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
@@ -157,71 +158,85 @@ pub struct GitsySettingsOutputs {
|
|||||||
pub repo_assets: Option<String>,
|
pub repo_assets: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
pub enum GitsySettingsExtraType {
|
||||||
|
repo_list,
|
||||||
|
summary,
|
||||||
|
history,
|
||||||
|
commit,
|
||||||
|
branches,
|
||||||
|
branch,
|
||||||
|
tags,
|
||||||
|
tag,
|
||||||
|
files,
|
||||||
|
file,
|
||||||
|
dir
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn substitute_path_vars<P,S>(path: &P, repo: Option<&GitRepo>, obj: Option<&S>) -> PathBuf
|
||||||
|
where P: AsRef<Path>,
|
||||||
|
S: SafePathVar {
|
||||||
|
let p: PathBuf = path.as_ref().to_path_buf();
|
||||||
|
assert!(p.is_relative(), "ERROR: path must be relative, not absolute: {}", p.display());
|
||||||
|
let p: PathBuf = repo.map(|r| r.safe_substitute(&p)).unwrap_or(p);
|
||||||
|
let p: PathBuf = obj.map(|o| o.safe_substitute(&p)).unwrap_or(p);
|
||||||
|
p
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
pub struct GitsySettingsExtraOutput {
|
||||||
|
pub template: String,
|
||||||
|
pub output: String,
|
||||||
|
pub kind: GitsySettingsExtraType,
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! output_path_fn {
|
macro_rules! output_path_fn {
|
||||||
($var:ident, $obj:ty, $id:ident, $is_dir:expr, $default:expr) => {
|
($var:ident, $is_dir:expr, $default:expr) => {
|
||||||
pub fn $var(&self, repo: Option<&GitRepo>, obj: Option<&$obj>) -> String {
|
pub fn $var<S: SafePathVar>(&self, repo: Option<&GitRepo>, obj: Option<&S>) -> PathBuf {
|
||||||
let tmpl_str = self.$var.as_deref().unwrap_or($default).to_string();
|
let tmpl_path = PathBuf::from(self.$var.as_deref().unwrap_or($default));
|
||||||
let tmpl_str = match (tmpl_str.contains("%REPO%"), repo.is_some()) {
|
let new_path = substitute_path_vars(&tmpl_path, repo, obj);
|
||||||
(true, true) => {
|
self.canonicalize_and_create(&new_path, $is_dir)
|
||||||
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);
|
|
||||||
}
|
|
||||||
_ => tmpl_str,
|
|
||||||
};
|
|
||||||
let tmpl_str = match (tmpl_str.contains("%ID%"), obj.is_some()) {
|
|
||||||
(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);
|
|
||||||
}
|
|
||||||
_ => tmpl_str,
|
|
||||||
};
|
|
||||||
let tmpl = PathBuf::from(tmpl_str);
|
|
||||||
let mut path = self.path.clone().canonicalize().expect(&format!(
|
|
||||||
"ERROR: unable to canonicalize output path: {}",
|
|
||||||
self.path.display()
|
|
||||||
));
|
|
||||||
path.push(tmpl);
|
|
||||||
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()))
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
//step_map_first!(boil_in_wort, Boil, Wort, |b: &Boil| { b.wort_start() });
|
|
||||||
|
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
impl GitsySettingsOutputs {
|
impl GitsySettingsOutputs {
|
||||||
output_path_fn!(repo_list, GitObject, full_hash, false, "index.html");
|
output_path_fn!(repo_list, false, "index.html");
|
||||||
output_path_fn!(summary, GitObject, full_hash, false, "%REPO%/index.html");
|
output_path_fn!(summary, false, "%REPO%/index.html");
|
||||||
output_path_fn!(history, GitObject, full_hash, false, "%REPO%/history%PAGE%.html");
|
output_path_fn!(history, false, "%REPO%/history%PAGE%.html");
|
||||||
output_path_fn!(commit, GitObject, full_hash, false, "%REPO%/commit/%ID%.html");
|
output_path_fn!(commit, false, "%REPO%/commit/%ID%.html");
|
||||||
output_path_fn!(branches, GitObject, full_hash, false, "%REPO%/branches%PAGE%.html");
|
output_path_fn!(branches, false, "%REPO%/branches%PAGE%.html");
|
||||||
output_path_fn!(branch, GitObject, full_hash, false, "%REPO%/branch/%ID%.html");
|
output_path_fn!(branch, false, "%REPO%/branch/%ID%.html");
|
||||||
output_path_fn!(tags, GitObject, full_hash, false, "%REPO%/tags%PAGE%.html");
|
output_path_fn!(tags, false, "%REPO%/tags%PAGE%.html");
|
||||||
output_path_fn!(tag, GitObject, full_hash, false, "%REPO%/tag/%ID%.html");
|
output_path_fn!(tag, false, "%REPO%/tag/%ID%.html");
|
||||||
output_path_fn!(files, GitObject, full_hash, false, "%REPO%/files.html");
|
output_path_fn!(files, false, "%REPO%/files.html");
|
||||||
output_path_fn!(file, GitFile, id, false, "%REPO%/file/%ID%.html");
|
output_path_fn!(file, false, "%REPO%/file/%ID%.html");
|
||||||
output_path_fn!(syntax_css, GitObject, full_hash, false, "%REPO%/file/syntax.css");
|
output_path_fn!(syntax_css, false, "%REPO%/file/syntax.css");
|
||||||
output_path_fn!(dir, GitFile, id, false, "%REPO%/dir/%ID%.html");
|
output_path_fn!(dir, false, "%REPO%/dir/%ID%.html");
|
||||||
output_path_fn!(error, GitObject, full_hash, false, "404.html");
|
output_path_fn!(error, false, "404.html");
|
||||||
output_path_fn!(global_assets, GitObject, full_hash, true, "assets/");
|
output_path_fn!(global_assets, true, "assets/");
|
||||||
output_path_fn!(repo_assets, GitObject, full_hash, true, "%REPO%/assets/");
|
output_path_fn!(repo_assets, true, "%REPO%/assets/");
|
||||||
|
|
||||||
|
fn canonicalize_and_create(&self, path: &Path, is_dir: bool) -> PathBuf {
|
||||||
|
let mut canonical_path = self.path.clone()
|
||||||
|
.canonicalize().expect(&format!(
|
||||||
|
"ERROR: unable to canonicalize output path: {}",
|
||||||
|
self.path.display()));
|
||||||
|
canonical_path.push(path);
|
||||||
|
match is_dir {
|
||||||
|
true => {
|
||||||
|
let _ = create_dir_all(&canonical_path);
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
if let Some(dir) = canonical_path.parent() {
|
||||||
|
let _ = create_dir_all(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
canonical_path
|
||||||
|
}
|
||||||
|
|
||||||
pub fn output_dir(&self) -> String {
|
pub fn output_dir(&self) -> String {
|
||||||
self.path.clone().canonicalize()
|
self.path.clone().canonicalize()
|
||||||
@@ -244,7 +259,10 @@ impl GitsySettingsOutputs {
|
|||||||
.expect(&format!("ERROR: failed to clean output directory: {}", dir.display()));
|
.expect(&format!("ERROR: failed to clean output directory: {}", dir.display()));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_relative(&self, path: &str) -> String {
|
pub fn to_relative<P: AsRef<Path>>(&self, path: &P) -> String {
|
||||||
|
let path = path.as_ref().to_str()
|
||||||
|
.expect(&format!("ERROR: Unable to make path relative: {}",
|
||||||
|
path.as_ref().display()));
|
||||||
let path_buf = PathBuf::from(path);
|
let path_buf = PathBuf::from(path);
|
||||||
path_buf.strip_prefix(self.output_dir())
|
path_buf.strip_prefix(self.output_dir())
|
||||||
.expect(&format!("ERROR: Unable to make path relative: {}", path))
|
.expect(&format!("ERROR: Unable to make path relative: {}", path))
|
||||||
|
|||||||
+5
-2
@@ -24,7 +24,7 @@ use crate::git::GitFile;
|
|||||||
use chrono::{naive::NaiveDateTime, offset::FixedOffset, DateTime};
|
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::{Path, PathBuf};
|
||||||
use tera::{from_value, 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 {
|
||||||
@@ -180,7 +180,10 @@ pub struct Pagination {
|
|||||||
pub prev_page: Option<String>,
|
pub prev_page: Option<String>,
|
||||||
}
|
}
|
||||||
impl Pagination {
|
impl Pagination {
|
||||||
pub fn new(cur: usize, total: usize, url_template: &str) -> Self {
|
pub fn new<P: AsRef<Path>>(cur: usize, total: usize, url_template: &P) -> Self {
|
||||||
|
let url_template = url_template.as_ref().to_str()
|
||||||
|
.expect(&format!("ERROR: attempted to paginate unparseable path: {}",
|
||||||
|
url_template.as_ref().display()));
|
||||||
let digits = total.to_string().len().max(2);
|
let digits = total.to_string().len().max(2);
|
||||||
let next = match cur + 1 <= total {
|
let next = match cur + 1 <= total {
|
||||||
true => Some(cur + 1),
|
true => Some(cur + 1),
|
||||||
|
|||||||
+21
@@ -20,6 +20,8 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
|
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
use std::path::Path;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::sync::atomic::AtomicUsize;
|
use std::sync::atomic::AtomicUsize;
|
||||||
|
|
||||||
pub static VERBOSITY: AtomicUsize = AtomicUsize::new(0);
|
pub static VERBOSITY: AtomicUsize = AtomicUsize::new(0);
|
||||||
@@ -130,3 +132,22 @@ impl From<tera::Error> for GitsyError {
|
|||||||
GitsyError::sourced_kind(GitsyErrorKind::Template, Some(&source.to_string()), source)
|
GitsyError::sourced_kind(GitsyErrorKind::Template, Some(&source.to_string()), source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sanitize_path_component(var: &str) -> String {
|
||||||
|
let nasty_chars = r###"<>:"/\|?*&"###.to_string() + "\n\t\0";
|
||||||
|
let mut safe_str = var.to_string();
|
||||||
|
safe_str.retain(|c| !nasty_chars.contains(c));
|
||||||
|
safe_str
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn urlify_path(path: &str) -> String {
|
||||||
|
let path = path
|
||||||
|
.replace("/", "+")
|
||||||
|
.replace("\\", "+");
|
||||||
|
path.trim().to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait SafePathVar {
|
||||||
|
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user