cargo fmt
This commit is contained in:
+420
-130
@@ -25,12 +25,14 @@ 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, HexFilter, MaskFilter, OctFilter, Pagination, TsDateFn, TsTimestampFn, UrlStringFilter},
|
template::{
|
||||||
|
DirFilter, FileFilter, HexFilter, MaskFilter, OctFilter, Pagination, TsDateFn, TsTimestampFn, UrlStringFilter,
|
||||||
|
},
|
||||||
util::{GitsyError, GitsyErrorKind, VERBOSITY},
|
util::{GitsyError, GitsyErrorKind, VERBOSITY},
|
||||||
};
|
};
|
||||||
|
use chrono::{DateTime, Local};
|
||||||
use git2::{Error, Repository};
|
use git2::{Error, Repository};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use chrono::{DateTime, Local};
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -102,8 +104,7 @@ impl GitsyGenerator {
|
|||||||
_ => Context::new(),
|
_ => Context::new(),
|
||||||
};
|
};
|
||||||
if let Some(extra) = &self.settings.extra {
|
if let Some(extra) = &self.settings.extra {
|
||||||
ctx
|
ctx.try_insert("extra", extra)
|
||||||
.try_insert("extra", extra)
|
|
||||||
.expect("Failed to add extra settings to template engine.");
|
.expect("Failed to add extra settings to template engine.");
|
||||||
}
|
}
|
||||||
if let Some(site_name) = &self.settings.site_name {
|
if let Some(site_name) = &self.settings.site_name {
|
||||||
@@ -117,7 +118,13 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
ctx.insert("site_dir", &self.settings.outputs.output_dir());
|
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() {
|
||||||
ctx.insert("site_assets", &self.settings.outputs.to_relative(&self.settings.outputs.global_assets::<GitFile>(None, None)));
|
ctx.insert(
|
||||||
|
"site_assets",
|
||||||
|
&self
|
||||||
|
.settings
|
||||||
|
.outputs
|
||||||
|
.to_relative(&self.settings.outputs.global_assets::<GitFile>(None, None)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
ctx.insert("site_generated_ts", &self.generated_dt.timestamp());
|
ctx.insert("site_generated_ts", &self.generated_dt.timestamp());
|
||||||
ctx.insert("site_generated_offset", &self.generated_dt.offset().local_minus_utc());
|
ctx.insert("site_generated_offset", &self.generated_dt.offset().local_minus_utc());
|
||||||
@@ -128,8 +135,13 @@ impl GitsyGenerator {
|
|||||||
let repo_path = match &repo_desc.path {
|
let repo_path = match &repo_desc.path {
|
||||||
url if url.starts_with("https://") || url.to_str().unwrap_or_default().contains("@") => {
|
url if url.starts_with("https://") || url.to_str().unwrap_or_default().contains("@") => {
|
||||||
if self.settings.outputs.cloned_repos.is_none() {
|
if self.settings.outputs.cloned_repos.is_none() {
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings,
|
return Err(GitsyError::kind(
|
||||||
Some(&format!("ERROR: Found remote repo [{}], but `cloned_repos` directory not configured.", name))));
|
GitsyErrorKind::Settings,
|
||||||
|
Some(&format!(
|
||||||
|
"ERROR: Found remote repo [{}], but `cloned_repos` directory not configured.",
|
||||||
|
name
|
||||||
|
)),
|
||||||
|
));
|
||||||
};
|
};
|
||||||
let clone_path: PathBuf = [self.settings.outputs.cloned_repos.as_deref().unwrap(), name]
|
let clone_path: PathBuf = [self.settings.outputs.cloned_repos.as_deref().unwrap(), name]
|
||||||
.iter()
|
.iter()
|
||||||
@@ -201,8 +213,10 @@ impl GitsyGenerator {
|
|||||||
name,
|
name,
|
||||||
dir.display()
|
dir.display()
|
||||||
);
|
);
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings,
|
return Err(GitsyError::kind(
|
||||||
Some(&format!("ERROR: Local repository not found: {}", name))));
|
GitsyErrorKind::Settings,
|
||||||
|
Some(&format!("ERROR: Local repository not found: {}", name)),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dir.to_string_lossy().to_string()
|
dir.to_string_lossy().to_string()
|
||||||
@@ -282,8 +296,11 @@ impl GitsyGenerator {
|
|||||||
|
|
||||||
fn write_rendered<P: AsRef<Path>>(&self, path: &P, rendered: &str) -> usize {
|
fn write_rendered<P: AsRef<Path>>(&self, path: &P, rendered: &str) -> usize {
|
||||||
let path: &Path = path.as_ref();
|
let path: &Path = path.as_ref();
|
||||||
assert!(self.settings.outputs.assert_valid(&path),
|
assert!(
|
||||||
"ERROR: attempted to write invalid path: {}", path.display());
|
self.settings.outputs.assert_valid(&path),
|
||||||
|
"ERROR: attempted to write invalid path: {}",
|
||||||
|
path.display()
|
||||||
|
);
|
||||||
// Write the file to disk
|
// Write the file to disk
|
||||||
let mut file = File::create(path).expect(&format!("Unable to write to output path: {}", path.display()));
|
let mut file = File::create(path).expect(&format!("Unable to write to output path: {}", path.display()));
|
||||||
file.write(rendered.as_bytes())
|
file.write(rendered.as_bytes())
|
||||||
@@ -312,8 +329,14 @@ impl GitsyGenerator {
|
|||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut global_bytes = 0;
|
let mut global_bytes = 0;
|
||||||
for (templ_path, out_path) in self.settings.outputs.repo_list::<GitRepo>(None, None) {
|
for (templ_path, out_path) in self.settings.outputs.repo_list::<GitRepo>(None, None) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes += self.write_rendered(&out_path, &rendered);
|
global_bytes += self.write_rendered(&out_path, &rendered);
|
||||||
@@ -330,8 +353,14 @@ impl GitsyGenerator {
|
|||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut global_bytes = 0;
|
let mut global_bytes = 0;
|
||||||
for (templ_path, out_path) in self.settings.outputs.error::<GitRepo>(None, None) {
|
for (templ_path, out_path) in self.settings.outputs.error::<GitRepo>(None, None) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes += self.write_rendered(&out_path, &rendered);
|
global_bytes += self.write_rendered(&out_path, &rendered);
|
||||||
@@ -344,13 +373,25 @@ impl GitsyGenerator {
|
|||||||
Ok(global_bytes)
|
Ok(global_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_summary(&self, ctx: &Context, atomic_bytes: &AtomicUsize,
|
pub fn gen_summary(
|
||||||
parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
for (templ_path, out_path) in self.settings.outputs.summary::<GitRepo>(Some(parsed_repo), None) {
|
for (templ_path, out_path) in self.settings.outputs.summary::<GitRepo>(Some(parsed_repo), None) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
let bytes = self.write_rendered(&out_path, &rendered);
|
let bytes = self.write_rendered(&out_path, &rendered);
|
||||||
@@ -361,27 +402,47 @@ impl GitsyGenerator {
|
|||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_history(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_history(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let repo_bytes = AtomicUsize::new(0);
|
let repo_bytes = AtomicUsize::new(0);
|
||||||
for (templ_path, out_path) in self.settings.outputs.history::<GitRepo>(Some(parsed_repo), None) {
|
for (templ_path, out_path) in self.settings.outputs.history::<GitRepo>(Some(parsed_repo), None) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
let pages = parsed_repo.history.chunks(self.settings.paginate_history());
|
let pages = parsed_repo.history.chunks(self.settings.paginate_history());
|
||||||
let page_count = pages.len();
|
let page_count = pages.len();
|
||||||
parsed_repo.history.par_chunks(self.settings.paginate_history()).enumerate().try_for_each(|(idx, page)| {
|
parsed_repo
|
||||||
|
.history
|
||||||
|
.par_chunks(self.settings.paginate_history())
|
||||||
|
.enumerate()
|
||||||
|
.try_for_each(|(idx, page)| {
|
||||||
let mut paged_ctx = ctx.clone();
|
let mut paged_ctx = ctx.clone();
|
||||||
let pagination = Pagination::new(
|
let pagination = Pagination::new(idx + 1, page_count, &out_path);
|
||||||
idx + 1,
|
|
||||||
page_count,
|
|
||||||
&out_path,
|
|
||||||
);
|
|
||||||
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);
|
||||||
let rendered = tera.render(templ_path, &paged_ctx)?;
|
let rendered = tera.render(templ_path, &paged_ctx)?;
|
||||||
@@ -390,25 +451,44 @@ impl GitsyGenerator {
|
|||||||
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
||||||
paged_ctx.remove("page");
|
paged_ctx.remove("page");
|
||||||
paged_ctx.remove("history");
|
paged_ctx.remove("history");
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
Ok::<(), GitsyError>(())
|
Ok::<(), GitsyError>(())
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
Ok(repo_bytes.load(Ordering::SeqCst))
|
Ok(repo_bytes.load(Ordering::SeqCst))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_commit(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_commit(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let mut ctx = ctx.clone();
|
let mut ctx = ctx.clone();
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
for (_id, commit) in &parsed_repo.commits {
|
for (_id, commit) in &parsed_repo.commits {
|
||||||
ctx
|
ctx.try_insert("commit", &commit)
|
||||||
.try_insert("commit", &commit)
|
|
||||||
.expect("Failed to add commit to template engine.");
|
.expect("Failed to add commit to template engine.");
|
||||||
for (templ_path, out_path) in self.settings.outputs.commit(Some(parsed_repo), Some(commit)) {
|
for (templ_path, out_path) in self.settings.outputs.commit(Some(parsed_repo), Some(commit)) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
let bytes = self.write_rendered(&out_path, &rendered);
|
let bytes = self.write_rendered(&out_path, &rendered);
|
||||||
@@ -421,28 +501,44 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.remove("commit");
|
ctx.remove("commit");
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_branches(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_branches(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
for (templ_path, out_path) in self.settings.outputs.branches::<GitRepo>(Some(parsed_repo), None) {
|
for (templ_path, out_path) in self.settings.outputs.branches::<GitRepo>(Some(parsed_repo), None) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
let mut paged_ctx = ctx.clone();
|
let mut paged_ctx = ctx.clone();
|
||||||
paged_ctx.remove("branches");
|
paged_ctx.remove("branches");
|
||||||
let pages = parsed_repo.branches.chunks(self.settings.paginate_branches());
|
let pages = parsed_repo.branches.chunks(self.settings.paginate_branches());
|
||||||
let page_count = pages.len();
|
let page_count = pages.len();
|
||||||
for (idx, page) in pages.enumerate() {
|
for (idx, page) in pages.enumerate() {
|
||||||
let pagination = Pagination::new(
|
let pagination = Pagination::new(idx + 1, page_count, &out_path);
|
||||||
idx + 1,
|
|
||||||
page_count,
|
|
||||||
&out_path,
|
|
||||||
);
|
|
||||||
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);
|
||||||
match tera.render(templ_path, &paged_ctx) {
|
match tera.render(templ_path, &paged_ctx) {
|
||||||
@@ -458,25 +554,44 @@ impl GitsyGenerator {
|
|||||||
paged_ctx.remove("page");
|
paged_ctx.remove("page");
|
||||||
paged_ctx.remove("branches");
|
paged_ctx.remove("branches");
|
||||||
}
|
}
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_branch(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_branch(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let mut ctx = ctx.clone();
|
let mut ctx = ctx.clone();
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
for branch in &parsed_repo.branches {
|
for branch in &parsed_repo.branches {
|
||||||
ctx.insert("branch", branch);
|
ctx.insert("branch", branch);
|
||||||
for (templ_path, out_path) in self.settings.outputs.branch(Some(parsed_repo), Some(branch)) {
|
for (templ_path, out_path) in self.settings.outputs.branch(Some(parsed_repo), Some(branch)) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
let bytes = self
|
let bytes = self.write_rendered(&out_path, &rendered);
|
||||||
.write_rendered(&out_path, &rendered);
|
|
||||||
repo_bytes += bytes;
|
repo_bytes += bytes;
|
||||||
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
@@ -486,25 +601,44 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.remove("branch");
|
ctx.remove("branch");
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_tags(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_tags(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
for (templ_path, out_path) in self.settings.outputs.tags::<GitRepo>(Some(parsed_repo), None) {
|
for (templ_path, out_path) in self.settings.outputs.tags::<GitRepo>(Some(parsed_repo), None) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
let mut paged_ctx = ctx.clone();
|
let mut paged_ctx = ctx.clone();
|
||||||
paged_ctx.remove("tags");
|
paged_ctx.remove("tags");
|
||||||
let pages = parsed_repo.tags.chunks(self.settings.paginate_tags());
|
let pages = parsed_repo.tags.chunks(self.settings.paginate_tags());
|
||||||
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, &out_path);
|
||||||
Pagination::new(idx + 1, page_count, &out_path);
|
|
||||||
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_path, &paged_ctx) {
|
match tera.render(templ_path, &paged_ctx) {
|
||||||
@@ -519,14 +653,28 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
paged_ctx.remove("page");
|
paged_ctx.remove("page");
|
||||||
paged_ctx.remove("tags");
|
paged_ctx.remove("tags");
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_tag(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_tag(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let mut ctx = ctx.clone();
|
let mut ctx = ctx.clone();
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
@@ -538,12 +686,17 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (templ_path, out_path) in self.settings.outputs.tag(Some(parsed_repo), Some(tag)) {
|
for (templ_path, out_path) in self.settings.outputs.tag(Some(parsed_repo), Some(tag)) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
let bytes =
|
let bytes = self.write_rendered(&out_path, &rendered);
|
||||||
self.write_rendered(&out_path, &rendered);
|
|
||||||
repo_bytes += bytes;
|
repo_bytes += bytes;
|
||||||
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
@@ -554,25 +707,44 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
ctx.remove("tag");
|
ctx.remove("tag");
|
||||||
ctx.remove("commit");
|
ctx.remove("commit");
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_files(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_files(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
_repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let mut ctx = ctx.clone();
|
let mut ctx = ctx.clone();
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
for (templ_path, out_path) in self.settings.outputs.files::<GitRepo>(Some(parsed_repo), None) {
|
for (templ_path, out_path) in self.settings.outputs.files::<GitRepo>(Some(parsed_repo), None) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
ctx.insert("root_files", &parsed_repo.root_files);
|
ctx.insert("root_files", &parsed_repo.root_files);
|
||||||
ctx.insert("all_files", &parsed_repo.all_files);
|
ctx.insert("all_files", &parsed_repo.all_files);
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
let bytes =
|
let bytes = self.write_rendered(&out_path, &rendered);
|
||||||
self.write_rendered(&out_path, &rendered);
|
|
||||||
repo_bytes += bytes;
|
repo_bytes += bytes;
|
||||||
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
@@ -580,13 +752,27 @@ impl GitsyGenerator {
|
|||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_file(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_file(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
|
|
||||||
@@ -604,8 +790,10 @@ impl GitsyGenerator {
|
|||||||
.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)
|
||||||
.expect("Invalid syntax highlighting theme specified.");
|
.expect("Invalid syntax highlighting theme specified.");
|
||||||
let bytes =
|
let bytes = self.write_rendered(
|
||||||
self.write_rendered(&self.settings.outputs.syntax_css::<GitFile>(Some(&parsed_repo), None), css.as_str());
|
&self.settings.outputs.syntax_css::<GitFile>(Some(&parsed_repo), None),
|
||||||
|
css.as_str(),
|
||||||
|
);
|
||||||
repo_bytes += bytes;
|
repo_bytes += bytes;
|
||||||
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
@@ -614,7 +802,10 @@ impl GitsyGenerator {
|
|||||||
// done first because syntax highlighting is very slow.
|
// done first because syntax highlighting is very slow.
|
||||||
let files: Vec<&GitFile> = parsed_repo.all_files.iter().filter(|x| x.kind == "file").collect();
|
let files: Vec<&GitFile> = parsed_repo.all_files.iter().filter(|x| x.kind == "file").collect();
|
||||||
let atomic_repo_bytes: AtomicUsize = AtomicUsize::new(repo_bytes);
|
let atomic_repo_bytes: AtomicUsize = AtomicUsize::new(repo_bytes);
|
||||||
let repo_path = repo.path().to_str().expect("ERROR: unable to determine path to local repository");
|
let repo_path = repo
|
||||||
|
.path()
|
||||||
|
.to_str()
|
||||||
|
.expect("ERROR: unable to determine path to local repository");
|
||||||
let _ = files
|
let _ = files
|
||||||
.par_iter()
|
.par_iter()
|
||||||
.fold(
|
.fold(
|
||||||
@@ -626,21 +817,32 @@ impl GitsyGenerator {
|
|||||||
|
|
||||||
let mut local_bytes = 0;
|
let mut local_bytes = 0;
|
||||||
let cur_repo_bytes = atomic_repo_bytes.load(Ordering::SeqCst);
|
let cur_repo_bytes = atomic_repo_bytes.load(Ordering::SeqCst);
|
||||||
size_check!(repo_desc, cur_repo_bytes, self.total_bytes.load(Ordering::SeqCst), return None);
|
size_check!(
|
||||||
|
repo_desc,
|
||||||
|
cur_repo_bytes,
|
||||||
|
self.total_bytes.load(Ordering::SeqCst),
|
||||||
|
return None
|
||||||
|
);
|
||||||
let file = match file.size < repo_desc.limit_file_size.unwrap_or(usize::MAX) {
|
let file = match file.size < repo_desc.limit_file_size.unwrap_or(usize::MAX) {
|
||||||
true => GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc)
|
true => {
|
||||||
.expect("Failed to parse file."),
|
GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc).expect("Failed to parse file.")
|
||||||
|
}
|
||||||
false => (*file).clone(),
|
false => (*file).clone(),
|
||||||
};
|
};
|
||||||
ctx
|
ctx.try_insert("file", &file)
|
||||||
.try_insert("file", &file)
|
|
||||||
.expect("Failed to add file to template engine.");
|
.expect("Failed to add file to template engine.");
|
||||||
for (templ_path, out_path) in self.settings.outputs.file(Some(parsed_repo), Some(&file)) {
|
for (templ_path, out_path) in self.settings.outputs.file(Some(parsed_repo), Some(&file)) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
local_bytes = self.write_rendered(&out_path, &rendered,);
|
local_bytes = self.write_rendered(&out_path, &rendered);
|
||||||
atomic_repo_bytes.fetch_add(local_bytes, Ordering::SeqCst);
|
atomic_repo_bytes.fetch_add(local_bytes, Ordering::SeqCst);
|
||||||
atomic_bytes.fetch_add(local_bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_add(local_bytes, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
@@ -659,28 +861,46 @@ impl GitsyGenerator {
|
|||||||
.while_some() // allow short-circuiting if size limit is reached
|
.while_some() // allow short-circuiting if size limit is reached
|
||||||
.sum::<usize>();
|
.sum::<usize>();
|
||||||
repo_bytes = atomic_repo_bytes.load(Ordering::SeqCst);
|
repo_bytes = atomic_repo_bytes.load(Ordering::SeqCst);
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn gen_dir(&self, ctx: &Context, atomic_bytes: &AtomicUsize, parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, repo: &Repository) -> Result<usize, GitsyError> {
|
pub fn gen_dir(
|
||||||
|
&self,
|
||||||
|
ctx: &Context,
|
||||||
|
atomic_bytes: &AtomicUsize,
|
||||||
|
parsed_repo: &GitRepo,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
repo: &Repository,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let mut ctx = ctx.clone();
|
let mut ctx = ctx.clone();
|
||||||
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
|
||||||
let mut repo_bytes = 0;
|
let mut repo_bytes = 0;
|
||||||
for dir in parsed_repo.all_files.iter().filter(|x| x.kind == "dir") {
|
for dir in parsed_repo.all_files.iter().filter(|x| x.kind == "dir") {
|
||||||
let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
|
let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
|
||||||
ctx.insert("dir", dir);
|
ctx.insert("dir", dir);
|
||||||
ctx
|
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.");
|
||||||
for (templ_path, out_path) in self.settings.outputs.dir(Some(parsed_repo), Some(dir)) {
|
for (templ_path, out_path) in self.settings.outputs.dir(Some(parsed_repo), Some(dir)) {
|
||||||
let templ_path = templ_path.to_str().expect(&format!("ERROR: a summary template path is invalid: {}", templ_path.display()));
|
let templ_path = templ_path.to_str().expect(&format!(
|
||||||
let out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
|
"ERROR: a summary template path is invalid: {}",
|
||||||
|
templ_path.display()
|
||||||
|
));
|
||||||
|
let out_path = out_path.to_str().expect(&format!(
|
||||||
|
"ERROR: a summary output path is invalid: {}",
|
||||||
|
out_path.display()
|
||||||
|
));
|
||||||
match tera.render(templ_path, &ctx) {
|
match tera.render(templ_path, &ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
let bytes =
|
let bytes = self.write_rendered(&out_path, &rendered);
|
||||||
self.write_rendered(&out_path, &rendered);
|
|
||||||
repo_bytes += bytes;
|
repo_bytes += bytes;
|
||||||
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
@@ -691,13 +911,25 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
ctx.remove("files");
|
ctx.remove("files");
|
||||||
ctx.remove("dir");
|
ctx.remove("dir");
|
||||||
size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
|
size_check_atomic!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
atomic_bytes,
|
||||||
|
self.total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(repo_bytes)
|
Ok(repo_bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_assets(&self, repo_desc: Option<&GitsySettingsRepo>, parsed_repo: Option<&GitRepo>, repo: Option<&Repository>) -> Result<usize, GitsyError> {
|
fn copy_assets(
|
||||||
|
&self,
|
||||||
|
repo_desc: Option<&GitsySettingsRepo>,
|
||||||
|
parsed_repo: Option<&GitRepo>,
|
||||||
|
repo: Option<&Repository>,
|
||||||
|
) -> Result<usize, GitsyError> {
|
||||||
let mut bytes = 0;
|
let mut bytes = 0;
|
||||||
match repo_desc {
|
match repo_desc {
|
||||||
Some(repo_desc) => {
|
Some(repo_desc) => {
|
||||||
@@ -725,7 +957,7 @@ impl GitsyGenerator {
|
|||||||
loud!(" - copied asset: {}", src_file.display());
|
loud!(" - copied asset: {}", src_file.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => {
|
_ => {
|
||||||
if self.settings.asset_files.is_some() {
|
if self.settings.asset_files.is_some() {
|
||||||
let target_dir = self.settings.outputs.global_assets::<GitFile>(None, None);
|
let target_dir = self.settings.outputs.global_assets::<GitFile>(None, None);
|
||||||
@@ -745,12 +977,16 @@ impl GitsyGenerator {
|
|||||||
loud!(" - copied asset: {}", src_file.display());
|
loud!(" - copied asset: {}", src_file.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn generate_repo(&self, repo_desc: &GitsySettingsRepo, pad_name_len: usize) -> Result<(GitRepo, usize), GitsyError> {
|
pub fn generate_repo(
|
||||||
|
&self,
|
||||||
|
repo_desc: &GitsySettingsRepo,
|
||||||
|
pad_name_len: usize,
|
||||||
|
) -> Result<(GitRepo, usize), GitsyError> {
|
||||||
loudest!("Repo settings:\n{:#?}", &repo_desc);
|
loudest!("Repo settings:\n{:#?}", &repo_desc);
|
||||||
let start_repo = Instant::now();
|
let start_repo = Instant::now();
|
||||||
|
|
||||||
@@ -779,15 +1015,16 @@ impl GitsyGenerator {
|
|||||||
for readme in readmes {
|
for readme in readmes {
|
||||||
if let Some(file) = parsed_repo.root_files.iter().filter(|x| &x.name == readme).next() {
|
if let Some(file) = parsed_repo.root_files.iter().filter(|x| &x.name == readme).next() {
|
||||||
louder!(" - found readme file: {}", file.name);
|
louder!(" - found readme file: {}", file.name);
|
||||||
let _ = GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc)
|
let _ =
|
||||||
.expect("Failed to parse file.");
|
GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc).expect("Failed to parse file.");
|
||||||
local_ctx.insert("readme", &file);
|
local_ctx.insert("readme", &file);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let fns = &[GitsyGenerator::gen_summary,
|
let fns = &[
|
||||||
|
GitsyGenerator::gen_summary,
|
||||||
GitsyGenerator::gen_branches,
|
GitsyGenerator::gen_branches,
|
||||||
GitsyGenerator::gen_branch,
|
GitsyGenerator::gen_branch,
|
||||||
GitsyGenerator::gen_tags,
|
GitsyGenerator::gen_tags,
|
||||||
@@ -799,7 +1036,9 @@ impl GitsyGenerator {
|
|||||||
GitsyGenerator::gen_files,
|
GitsyGenerator::gen_files,
|
||||||
];
|
];
|
||||||
|
|
||||||
let repo_bytes: usize = fns.par_iter().try_fold(
|
let repo_bytes: usize = fns
|
||||||
|
.par_iter()
|
||||||
|
.try_fold(
|
||||||
|| 0,
|
|| 0,
|
||||||
|acc, x| {
|
|acc, x| {
|
||||||
let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
|
let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
|
||||||
@@ -808,11 +1047,19 @@ impl GitsyGenerator {
|
|||||||
atomic_bytes.fetch_sub(bytes, Ordering::SeqCst);
|
atomic_bytes.fetch_sub(bytes, Ordering::SeqCst);
|
||||||
self.total_bytes.fetch_add(bytes, Ordering::SeqCst);
|
self.total_bytes.fetch_add(bytes, Ordering::SeqCst);
|
||||||
Ok::<usize, GitsyError>(acc + bytes)
|
Ok::<usize, GitsyError>(acc + bytes)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
|
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
|
||||||
|
|
||||||
size_check!(repo_desc, 0, self.total_bytes.load(Ordering::SeqCst),
|
size_check!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
|
repo_desc,
|
||||||
|
0,
|
||||||
|
self.total_bytes.load(Ordering::SeqCst),
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
self.copy_assets(Some(&repo_desc), Some(&parsed_repo), Some(&repo))?;
|
self.copy_assets(Some(&repo_desc), Some(&parsed_repo), Some(&repo))?;
|
||||||
|
|
||||||
@@ -878,45 +1125,81 @@ impl GitsyGenerator {
|
|||||||
let mut tb = 0;
|
let mut tb = 0;
|
||||||
for repo_desc in &repo_vec {
|
for repo_desc in &repo_vec {
|
||||||
let (minimized_repo, repo_bytes) = self.generate_repo(repo_desc, longest_repo_name)?;
|
let (minimized_repo, repo_bytes) = self.generate_repo(repo_desc, longest_repo_name)?;
|
||||||
size_check!(repo_desc, 0, tb,
|
size_check!(
|
||||||
return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: site size limit exceeded"))));
|
repo_desc,
|
||||||
|
0,
|
||||||
|
tb,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Settings,
|
||||||
|
Some("ERROR: site size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
shared_repos.lock().unwrap().push(minimized_repo);
|
shared_repos.lock().unwrap().push(minimized_repo);
|
||||||
tb += repo_bytes;
|
tb += repo_bytes;
|
||||||
}
|
}
|
||||||
tb
|
tb
|
||||||
},
|
}
|
||||||
n if n == 0 => {
|
n if n == 0 => {
|
||||||
let total_bytes: usize = repo_vec.par_iter().try_fold(|| 0, |acc, repo_desc| {
|
let total_bytes: usize = repo_vec
|
||||||
|
.par_iter()
|
||||||
|
.try_fold(
|
||||||
|
|| 0,
|
||||||
|
|acc, repo_desc| {
|
||||||
let (minimized_repo, repo_bytes) = self.generate_repo(repo_desc, longest_repo_name)?;
|
let (minimized_repo, repo_bytes) = self.generate_repo(repo_desc, longest_repo_name)?;
|
||||||
size_check!(repo_desc, 0, acc + repo_bytes, return Err(GitsyError::kind(GitsyErrorKind::Unknown,
|
size_check!(
|
||||||
Some("ERROR: site size limit exceeded"))));
|
repo_desc,
|
||||||
|
0,
|
||||||
|
acc + repo_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Unknown,
|
||||||
|
Some("ERROR: site size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
shared_repos.lock().unwrap().push(minimized_repo);
|
shared_repos.lock().unwrap().push(minimized_repo);
|
||||||
Ok::<usize, GitsyError>(repo_bytes)
|
Ok::<usize, GitsyError>(repo_bytes)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
|
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
|
||||||
total_bytes
|
total_bytes
|
||||||
},
|
}
|
||||||
n => {
|
n => {
|
||||||
let pool = rayon::ThreadPoolBuilder::new()
|
let pool = rayon::ThreadPoolBuilder::new().num_threads(n).build().unwrap();
|
||||||
.num_threads(n)
|
|
||||||
.build().unwrap();
|
|
||||||
|
|
||||||
let total_bytes = pool.install(|| {
|
let total_bytes = pool.install(|| {
|
||||||
let total_bytes: usize = repo_vec.par_iter().try_fold(|| 0, |acc, repo_desc| {
|
let total_bytes: usize = repo_vec
|
||||||
|
.par_iter()
|
||||||
|
.try_fold(
|
||||||
|
|| 0,
|
||||||
|
|acc, repo_desc| {
|
||||||
let (minimized_repo, repo_bytes) = self.generate_repo(repo_desc, longest_repo_name)?;
|
let (minimized_repo, repo_bytes) = self.generate_repo(repo_desc, longest_repo_name)?;
|
||||||
size_check!(repo_desc, 0, acc + repo_bytes, return Err(GitsyError::kind(GitsyErrorKind::Unknown,
|
size_check!(
|
||||||
Some("ERROR: site size limit exceeded"))));
|
repo_desc,
|
||||||
|
0,
|
||||||
|
acc + repo_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Unknown,
|
||||||
|
Some("ERROR: site size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
shared_repos.lock().unwrap().push(minimized_repo);
|
shared_repos.lock().unwrap().push(minimized_repo);
|
||||||
Ok::<usize, GitsyError>(repo_bytes)
|
Ok::<usize, GitsyError>(repo_bytes)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
|
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
|
||||||
Ok::<usize, GitsyError>(total_bytes)
|
Ok::<usize, GitsyError>(total_bytes)
|
||||||
})?;
|
})?;
|
||||||
total_bytes
|
total_bytes
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
size_check!(self.settings, 0, total_bytes, return Err(GitsyError::kind(GitsyErrorKind::Unknown,
|
size_check!(
|
||||||
Some("ERROR: site size limit exceeded"))));
|
self.settings,
|
||||||
|
0,
|
||||||
|
total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Unknown,
|
||||||
|
Some("ERROR: site size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
|
|
||||||
let repos = shared_repos;
|
let repos = shared_repos;
|
||||||
|
|
||||||
@@ -936,8 +1219,15 @@ impl GitsyGenerator {
|
|||||||
self.copy_assets(None, None, None)?;
|
self.copy_assets(None, None, None)?;
|
||||||
|
|
||||||
total_bytes += global_bytes;
|
total_bytes += global_bytes;
|
||||||
size_check!(self.settings, 0, total_bytes, return Err(GitsyError::kind(GitsyErrorKind::Unknown,
|
size_check!(
|
||||||
Some("ERROR: site size limit exceeded"))));
|
self.settings,
|
||||||
|
0,
|
||||||
|
total_bytes,
|
||||||
|
return Err(GitsyError::kind(
|
||||||
|
GitsyErrorKind::Unknown,
|
||||||
|
Some("ERROR: site size limit exceeded")
|
||||||
|
))
|
||||||
|
);
|
||||||
normal!(
|
normal!(
|
||||||
"done in {:.2}s ({} bytes)",
|
"done in {:.2}s ({} bytes)",
|
||||||
start_global.elapsed().as_secs_f32(),
|
start_global.elapsed().as_secs_f32(),
|
||||||
|
|||||||
+83
-42
@@ -21,16 +21,16 @@
|
|||||||
* 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::util::{sanitize_path_component, urlify_path, SafePathVar};
|
||||||
use crate::{error, loud, louder, loudest};
|
use crate::{error, loud, louder, loudest};
|
||||||
use git2::{DiffOptions, Error, Repository};
|
use git2::{DiffOptions, Error, Repository};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::atomic::AtomicUsize;
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::atomic::AtomicUsize;
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
|
|
||||||
fn first_line(msg: &[u8]) -> String {
|
fn first_line(msg: &[u8]) -> String {
|
||||||
@@ -63,8 +63,10 @@ impl GitRepo {
|
|||||||
let new_history: Vec<GitObject> = self.history.iter().cloned().take(max_entries).collect();
|
let new_history: Vec<GitObject> = self.history.iter().cloned().take(max_entries).collect();
|
||||||
for entry in &new_history {
|
for entry in &new_history {
|
||||||
if self.commits.contains_key(&entry.full_hash) {
|
if self.commits.contains_key(&entry.full_hash) {
|
||||||
new_commits.insert(entry.full_hash.clone(),
|
new_commits.insert(
|
||||||
self.commits.get(&entry.full_hash).unwrap().clone());
|
entry.full_hash.clone(),
|
||||||
|
self.commits.get(&entry.full_hash).unwrap().clone(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let all_files: Vec<GitFile> = self.all_files.iter().cloned().take(max_entries).collect();
|
let all_files: Vec<GitFile> = self.all_files.iter().cloned().take(max_entries).collect();
|
||||||
@@ -99,8 +101,11 @@ impl SafePathVar for GitRepo {
|
|||||||
let cmp = cmp.as_os_str().to_string_lossy().replace("%REPO%", &safe_name);
|
let cmp = cmp.as_os_str().to_string_lossy().replace("%REPO%", &safe_name);
|
||||||
dst.push(cmp);
|
dst.push(cmp);
|
||||||
}
|
}
|
||||||
assert!(src.components().count() == dst.components().count(),
|
assert!(
|
||||||
"ERROR: path substitution accidentally created a new folder in: {}", src.display());
|
src.components().count() == dst.components().count(),
|
||||||
|
"ERROR: path substitution accidentally created a new folder in: {}",
|
||||||
|
src.display()
|
||||||
|
);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,17 +149,24 @@ impl SafePathVar for GitObject {
|
|||||||
let src: &Path = path.as_ref();
|
let src: &Path = path.as_ref();
|
||||||
let mut dst = PathBuf::new();
|
let mut dst = PathBuf::new();
|
||||||
let safe_full_hash = sanitize_path_component(&self.full_hash);
|
let safe_full_hash = sanitize_path_component(&self.full_hash);
|
||||||
let safe_ref = self.ref_name.as_deref()
|
let safe_ref = self
|
||||||
|
.ref_name
|
||||||
|
.as_deref()
|
||||||
.map(|v| sanitize_path_component(&urlify_path(v)))
|
.map(|v| sanitize_path_component(&urlify_path(v)))
|
||||||
.unwrap_or("%REF%".to_string());
|
.unwrap_or("%REF%".to_string());
|
||||||
for cmp in src.components() {
|
for cmp in src.components() {
|
||||||
let cmp = cmp.as_os_str().to_string_lossy()
|
let cmp = cmp
|
||||||
|
.as_os_str()
|
||||||
|
.to_string_lossy()
|
||||||
.replace("%ID%", &safe_full_hash)
|
.replace("%ID%", &safe_full_hash)
|
||||||
.replace("%REF%", &safe_ref);
|
.replace("%REF%", &safe_ref);
|
||||||
dst.push(cmp);
|
dst.push(cmp);
|
||||||
}
|
}
|
||||||
assert!(src.components().count() == dst.components().count(),
|
assert!(
|
||||||
"ERROR: path substitution accidentally created a new folder in: {}", src.display());
|
src.components().count() == dst.components().count(),
|
||||||
|
"ERROR: path substitution accidentally created a new folder in: {}",
|
||||||
|
src.display()
|
||||||
|
);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,14 +201,19 @@ impl SafePathVar for GitFile {
|
|||||||
let safe_name = sanitize_path_component(&self.name);
|
let safe_name = sanitize_path_component(&self.name);
|
||||||
let safe_path = sanitize_path_component(&urlify_path(&self.path));
|
let safe_path = sanitize_path_component(&urlify_path(&self.path));
|
||||||
for cmp in src.components() {
|
for cmp in src.components() {
|
||||||
let cmp = cmp.as_os_str().to_string_lossy()
|
let cmp = cmp
|
||||||
|
.as_os_str()
|
||||||
|
.to_string_lossy()
|
||||||
.replace("%ID%", &safe_id)
|
.replace("%ID%", &safe_id)
|
||||||
.replace("%NAME%", &safe_name)
|
.replace("%NAME%", &safe_name)
|
||||||
.replace("%PATH%", &safe_path);
|
.replace("%PATH%", &safe_path);
|
||||||
dst.push(cmp);
|
dst.push(cmp);
|
||||||
}
|
}
|
||||||
assert!(src.components().count() == dst.components().count(),
|
assert!(
|
||||||
"ERROR: path substitution accidentally created a new folder in: {}", src.display());
|
src.components().count() == dst.components().count(),
|
||||||
|
"ERROR: path substitution accidentally created a new folder in: {}",
|
||||||
|
src.display()
|
||||||
|
);
|
||||||
dst
|
dst
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -295,11 +312,24 @@ fn walk_file_tree(
|
|||||||
|
|
||||||
pub fn dir_listing(repo: &Repository, file: &GitFile) -> Result<Vec<GitFile>, Error> {
|
pub fn dir_listing(repo: &Repository, file: &GitFile) -> Result<Vec<GitFile>, Error> {
|
||||||
let mut files: Vec<GitFile> = vec![];
|
let mut files: Vec<GitFile> = vec![];
|
||||||
walk_file_tree(&repo, &file.id, &mut files, 0, usize::MAX, false, &(file.path.clone() + "/"))?;
|
walk_file_tree(
|
||||||
|
&repo,
|
||||||
|
&file.id,
|
||||||
|
&mut files,
|
||||||
|
0,
|
||||||
|
usize::MAX,
|
||||||
|
false,
|
||||||
|
&(file.path.clone() + "/"),
|
||||||
|
)?;
|
||||||
Ok(files)
|
Ok(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_revwalk(repo: &Repository, mut revwalk: git2::Revwalk, references: &BTreeMap<String, Vec<String>>, settings: &GitsySettingsRepo) -> Result<Vec<GitObject>, Error> {
|
pub fn parse_revwalk(
|
||||||
|
repo: &Repository,
|
||||||
|
mut revwalk: git2::Revwalk,
|
||||||
|
references: &BTreeMap<String, Vec<String>>,
|
||||||
|
settings: &GitsySettingsRepo,
|
||||||
|
) -> Result<Vec<GitObject>, Error> {
|
||||||
let mut history: Vec<GitObject> = vec![];
|
let mut history: Vec<GitObject> = vec![];
|
||||||
|
|
||||||
for (idx, oid) in revwalk.by_ref().enumerate() {
|
for (idx, oid) in revwalk.by_ref().enumerate() {
|
||||||
@@ -308,8 +338,12 @@ pub fn parse_revwalk(repo: &Repository, mut revwalk: git2::Revwalk, references:
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let parsed = parse_commit(idx, settings, repo, &oid.to_string(), &references)?;
|
let parsed = parse_commit(idx, settings, repo, &oid.to_string(), &references)?;
|
||||||
loudest!(" + [{}] {} {}", idx, parsed.full_hash,
|
loudest!(
|
||||||
parsed.summary.as_deref().unwrap_or_default());
|
" + [{}] {} {}",
|
||||||
|
idx,
|
||||||
|
parsed.full_hash,
|
||||||
|
parsed.summary.as_deref().unwrap_or_default()
|
||||||
|
);
|
||||||
history.push(parsed);
|
history.push(parsed);
|
||||||
}
|
}
|
||||||
Ok(history)
|
Ok(history)
|
||||||
@@ -372,8 +406,7 @@ pub fn parse_repo(
|
|||||||
// Let's arbitrarily say it's not worth parallelizing unless we
|
// Let's arbitrarily say it's not worth parallelizing unless we
|
||||||
// can give all cores at least 1k commits to parse. This could
|
// can give all cores at least 1k commits to parse. This could
|
||||||
// certainly use some configurability...
|
// certainly use some configurability...
|
||||||
let thread_jobs = match rayon::current_num_threads() > 1 &&
|
let thread_jobs = match rayon::current_num_threads() > 1 && commit_count > 1000 * rayon::current_num_threads() {
|
||||||
commit_count > 1000 * rayon::current_num_threads() {
|
|
||||||
// Divide a chunk up into even smaller units, so each core
|
// Divide a chunk up into even smaller units, so each core
|
||||||
// runs about 10. This makes it more efficient to detect when
|
// runs about 10. This makes it more efficient to detect when
|
||||||
// the commit limit is reached and short-circuit.
|
// the commit limit is reached and short-circuit.
|
||||||
@@ -388,14 +421,23 @@ pub fn parse_repo(
|
|||||||
// might not be evenly distributed.
|
// might not be evenly distributed.
|
||||||
let chunk_size = ((commit_count as f64) / (thread_jobs as f64)).ceil() as usize;
|
let chunk_size = ((commit_count as f64) / (thread_jobs as f64)).ceil() as usize;
|
||||||
if thread_jobs > 1 {
|
if thread_jobs > 1 {
|
||||||
loud!(" - splitting {} commits across {} threads of approximate size {}", commit_count, thread_jobs, chunk_size);
|
loud!(
|
||||||
|
" - splitting {} commits across {} threads of approximate size {}",
|
||||||
|
commit_count,
|
||||||
|
thread_jobs,
|
||||||
|
chunk_size
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
let repo_path = repo.path();
|
let repo_path = repo.path();
|
||||||
|
|
||||||
let thread_jobs: Vec<usize> = (0..thread_jobs).rev().collect(); // note the subtle rev() to do this in the right order
|
let thread_jobs: Vec<usize> = (0..thread_jobs).rev().collect(); // note the subtle rev() to do this in the right order
|
||||||
let atomic_commits = AtomicUsize::new(0);
|
let atomic_commits = AtomicUsize::new(0);
|
||||||
let mut history: Vec<_> = thread_jobs.par_iter().try_fold(|| Vec::<_>::new(), |mut acc, thread| {
|
let mut history: Vec<_> = thread_jobs
|
||||||
|
.par_iter()
|
||||||
|
.try_fold(
|
||||||
|
|| Vec::<_>::new(),
|
||||||
|
|mut acc, thread| {
|
||||||
if atomic_commits.load(Ordering::SeqCst) > settings.limit_history.unwrap_or(usize::MAX) {
|
if atomic_commits.load(Ordering::SeqCst) > settings.limit_history.unwrap_or(usize::MAX) {
|
||||||
// TODO: should convert all error paths in this function
|
// TODO: should convert all error paths in this function
|
||||||
// to GitsyErrors, and differentiate between real failures
|
// to GitsyErrors, and differentiate between real failures
|
||||||
@@ -419,9 +461,7 @@ pub fn parse_repo(
|
|||||||
true => "".into(),
|
true => "".into(),
|
||||||
false => format!("~{}", start_commit - chunk_size),
|
false => format!("~{}", start_commit - chunk_size),
|
||||||
};
|
};
|
||||||
let range = format!("{}~{}..{}{}",
|
let range = format!("{}~{}..{}{}", branch_name, start_commit, branch_name, end_commit);
|
||||||
branch_name, start_commit,
|
|
||||||
branch_name, end_commit);
|
|
||||||
loud!(" - Parse range: {} on thread {}", range, thread);
|
loud!(" - Parse range: {} on thread {}", range, thread);
|
||||||
match *thread == 0 {
|
match *thread == 0 {
|
||||||
true => {
|
true => {
|
||||||
@@ -431,7 +471,7 @@ pub fn parse_repo(
|
|||||||
let end_commit = format!("{}{}", branch_name, end_commit);
|
let end_commit = format!("{}{}", branch_name, end_commit);
|
||||||
let branch_obj = repo.revparse_single(&end_commit).unwrap();
|
let branch_obj = repo.revparse_single(&end_commit).unwrap();
|
||||||
revwalk.push(branch_obj.id())?
|
revwalk.push(branch_obj.id())?
|
||||||
},
|
}
|
||||||
false => revwalk.push_range(&range)?,
|
false => revwalk.push_range(&range)?,
|
||||||
}
|
}
|
||||||
let res = parse_revwalk(&repo, revwalk, &references, &settings)?;
|
let res = parse_revwalk(&repo, revwalk, &references, &settings)?;
|
||||||
@@ -439,7 +479,8 @@ pub fn parse_repo(
|
|||||||
atomic_commits.fetch_add(res.len(), Ordering::SeqCst);
|
atomic_commits.fetch_add(res.len(), Ordering::SeqCst);
|
||||||
acc.extend(res);
|
acc.extend(res);
|
||||||
Ok(acc)
|
Ok(acc)
|
||||||
})
|
},
|
||||||
|
)
|
||||||
.map(|x: Result<Vec<GitObject>, Error>| x.ok())
|
.map(|x: Result<Vec<GitObject>, Error>| x.ok())
|
||||||
.while_some()
|
.while_some()
|
||||||
.flatten_iter() // concatenate all of the vecs in series
|
.flatten_iter() // concatenate all of the vecs in series
|
||||||
@@ -581,9 +622,13 @@ pub fn parse_repo(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_commit(idx: usize, settings: &GitsySettingsRepo,
|
pub fn parse_commit(
|
||||||
repo: &Repository, refr: &str,
|
idx: usize,
|
||||||
references: &BTreeMap<String, Vec<String>>) -> Result<GitObject, Error> {
|
settings: &GitsySettingsRepo,
|
||||||
|
repo: &Repository,
|
||||||
|
refr: &str,
|
||||||
|
references: &BTreeMap<String, Vec<String>>,
|
||||||
|
) -> Result<GitObject, Error> {
|
||||||
let obj = repo.revparse_single(refr)?;
|
let obj = repo.revparse_single(refr)?;
|
||||||
let commit = repo.find_commit(obj.id())?;
|
let commit = repo.find_commit(obj.id())?;
|
||||||
|
|
||||||
@@ -610,9 +655,7 @@ pub fn parse_commit(idx: usize, settings: &GitsySettingsRepo,
|
|||||||
};
|
};
|
||||||
|
|
||||||
let (stats, commit_diff) = match idx < settings.limit_diffs.unwrap_or(usize::MAX) {
|
let (stats, commit_diff) = match idx < settings.limit_diffs.unwrap_or(usize::MAX) {
|
||||||
false => {
|
false => (None, None),
|
||||||
(None, None)
|
|
||||||
},
|
|
||||||
true => {
|
true => {
|
||||||
let b = commit.tree()?;
|
let b = commit.tree()?;
|
||||||
let mut diffopts = DiffOptions::new();
|
let mut diffopts = DiffOptions::new();
|
||||||
@@ -620,17 +663,13 @@ pub fn parse_commit(idx: usize, settings: &GitsySettingsRepo,
|
|||||||
let diff = repo.diff_tree_to_tree(a.as_ref(), Some(&b), Some(&mut diffopts))?;
|
let diff = repo.diff_tree_to_tree(a.as_ref(), Some(&b), Some(&mut diffopts))?;
|
||||||
let stats = diff.stats()?;
|
let stats = diff.stats()?;
|
||||||
let commit_diff: Option<GitDiffCommit> = match idx < settings.limit_diffs.unwrap_or(usize::MAX) {
|
let commit_diff: Option<GitDiffCommit> = match idx < settings.limit_diffs.unwrap_or(usize::MAX) {
|
||||||
true => {
|
true => Some(GitDiffCommit {
|
||||||
Some(GitDiffCommit {
|
|
||||||
file_count: stats.files_changed(),
|
file_count: stats.files_changed(),
|
||||||
additions: stats.insertions(),
|
additions: stats.insertions(),
|
||||||
deletions: stats.deletions(),
|
deletions: stats.deletions(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
}),
|
||||||
},
|
false => None,
|
||||||
false => {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let stats = GitStats {
|
let stats = GitStats {
|
||||||
files: stats.files_changed(),
|
files: stats.files_changed(),
|
||||||
@@ -681,7 +720,8 @@ pub fn parse_commit(idx: usize, settings: &GitsySettingsRepo,
|
|||||||
None, // TODO: handle binary files?
|
None, // TODO: handle binary files?
|
||||||
Some(&mut |_file, hunk| {
|
Some(&mut |_file, hunk| {
|
||||||
let mut files = files.borrow_mut();
|
let mut files = files.borrow_mut();
|
||||||
let file_diff: &mut GitDiffFile = files.last_mut().expect("Diff hunk not associated with a file!");
|
let file_diff: &mut GitDiffFile =
|
||||||
|
files.last_mut().expect("Diff hunk not associated with a file!");
|
||||||
let mut hunk_diff: GitDiffHunk = Default::default();
|
let mut hunk_diff: GitDiffHunk = Default::default();
|
||||||
hunk_diff.context = String::from_utf8_lossy(hunk.header()).to_string();
|
hunk_diff.context = String::from_utf8_lossy(hunk.header()).to_string();
|
||||||
file_diff.hunks.push(hunk_diff);
|
file_diff.hunks.push(hunk_diff);
|
||||||
@@ -689,7 +729,8 @@ pub fn parse_commit(idx: usize, settings: &GitsySettingsRepo,
|
|||||||
}),
|
}),
|
||||||
Some(&mut |_file, _hunk, line| {
|
Some(&mut |_file, _hunk, line| {
|
||||||
let mut files = files.borrow_mut();
|
let mut files = files.borrow_mut();
|
||||||
let file_diff: &mut GitDiffFile = files.last_mut().expect("Diff hunk not associated with a file!");
|
let file_diff: &mut GitDiffFile =
|
||||||
|
files.last_mut().expect("Diff hunk not associated with a file!");
|
||||||
let hunk_diff: &mut GitDiffHunk = file_diff
|
let hunk_diff: &mut GitDiffHunk = file_diff
|
||||||
.hunks
|
.hunks
|
||||||
.last_mut()
|
.last_mut()
|
||||||
@@ -727,7 +768,7 @@ pub fn parse_commit(idx: usize, settings: &GitsySettingsRepo,
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
(Some(stats), commit_diff)
|
(Some(stats), commit_diff)
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let tree = obj.peel_to_tree()?;
|
let tree = obj.peel_to_tree()?;
|
||||||
|
|||||||
+32
-21
@@ -20,14 +20,14 @@
|
|||||||
* 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 crate::{louder, error};
|
|
||||||
use crate::git::GitRepo;
|
use crate::git::GitRepo;
|
||||||
use crate::util::SafePathVar;
|
use crate::util::SafePathVar;
|
||||||
|
use crate::{error, louder};
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use git2::Repository;
|
use git2::Repository;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
use std::fs::{create_dir, create_dir_all, read_dir, remove_dir_all, read_to_string};
|
use std::fs::{create_dir, create_dir_all, read_dir, read_to_string, remove_dir_all};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::Ordering;
|
use std::sync::atomic::Ordering;
|
||||||
@@ -140,11 +140,17 @@ pub enum GitsyTemplateType {
|
|||||||
error,
|
error,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn substitute_path_vars<P,S>(path: &P, repo: Option<&GitRepo>, obj: Option<&S>) -> PathBuf
|
pub fn substitute_path_vars<P, S>(path: &P, repo: Option<&GitRepo>, obj: Option<&S>) -> PathBuf
|
||||||
where P: AsRef<Path>,
|
where
|
||||||
S: SafePathVar {
|
P: AsRef<Path>,
|
||||||
|
S: SafePathVar,
|
||||||
|
{
|
||||||
let p: PathBuf = path.as_ref().to_path_buf();
|
let p: PathBuf = path.as_ref().to_path_buf();
|
||||||
assert!(p.is_relative(), "ERROR: path must be relative, not absolute: {}", p.display());
|
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 = repo.map(|r| r.safe_substitute(&p)).unwrap_or(p);
|
||||||
let p: PathBuf = obj.map(|o| o.safe_substitute(&p)).unwrap_or(p);
|
let p: PathBuf = obj.map(|o| o.safe_substitute(&p)).unwrap_or(p);
|
||||||
p
|
p
|
||||||
@@ -164,42 +170,45 @@ macro_rules! template_fn {
|
|||||||
let new_path = substitute_path_vars(&tmpl_path, repo, obj);
|
let new_path = substitute_path_vars(&tmpl_path, repo, obj);
|
||||||
self.canonicalize_and_create(&new_path, $is_dir)
|
self.canonicalize_and_create(&new_path, $is_dir)
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! templates_fn {
|
macro_rules! templates_fn {
|
||||||
($var:ident, $is_dir:expr) => {
|
($var:ident, $is_dir:expr) => {
|
||||||
pub fn $var<S: SafePathVar>(&self, repo: Option<&GitRepo>, obj: Option<&S>) -> Vec<(PathBuf, PathBuf)> {
|
pub fn $var<S: SafePathVar>(&self, repo: Option<&GitRepo>, obj: Option<&S>) -> Vec<(PathBuf, PathBuf)> {
|
||||||
match &self.templates {
|
match &self.templates {
|
||||||
Some(template) => {
|
Some(template) => template
|
||||||
template.iter()
|
.iter()
|
||||||
.filter(|x| x.kind == GitsyTemplateType::$var)
|
.filter(|x| x.kind == GitsyTemplateType::$var)
|
||||||
.map(|x| {
|
.map(|x| {
|
||||||
let tmpl_path = PathBuf::from(&x.output);
|
let tmpl_path = PathBuf::from(&x.output);
|
||||||
let new_path = substitute_path_vars(&tmpl_path, repo, obj);
|
let new_path = substitute_path_vars(&tmpl_path, repo, obj);
|
||||||
(PathBuf::from(&x.template),
|
(
|
||||||
self.canonicalize_and_create(&new_path, $is_dir))
|
PathBuf::from(&x.template),
|
||||||
}).collect()
|
self.canonicalize_and_create(&new_path, $is_dir),
|
||||||
},
|
)
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
None => {
|
None => {
|
||||||
vec!()
|
vec![]
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SafePathVar for GitsySettingsOutputs {
|
impl SafePathVar for GitsySettingsOutputs {
|
||||||
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf {
|
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf {
|
||||||
let src: &Path = path.as_ref();
|
let src: &Path = path.as_ref();
|
||||||
let mut dst = PathBuf::new();
|
let mut dst = PathBuf::new();
|
||||||
let root = self.template_root.to_str()
|
let root = self.template_root.to_str().expect(&format!(
|
||||||
.expect(&format!("ERROR: couldn't parse template root: {}", self.template_root.display()));
|
"ERROR: couldn't parse template root: {}",
|
||||||
|
self.template_root.display()
|
||||||
|
));
|
||||||
for cmp in src.components() {
|
for cmp in src.components() {
|
||||||
// NOTE: this variable is not sanitized, since it's
|
// NOTE: this variable is not sanitized, since it's
|
||||||
// allowed to create new directory structure.
|
// allowed to create new directory structure.
|
||||||
let cmp = cmp.as_os_str().to_string_lossy()
|
let cmp = cmp.as_os_str().to_string_lossy().replace("%TEMPLATE%", &root);
|
||||||
.replace("%TEMPLATE%", &root);
|
|
||||||
dst.push(cmp);
|
dst.push(cmp);
|
||||||
}
|
}
|
||||||
dst
|
dst
|
||||||
@@ -545,9 +554,11 @@ impl GitsySettings {
|
|||||||
if cli.repos.len() > 0 {
|
if cli.repos.len() > 0 {
|
||||||
repo_descriptions.clear();
|
repo_descriptions.clear();
|
||||||
for dir in &cli.repos {
|
for dir in &cli.repos {
|
||||||
let name: String = dir.file_name()
|
let name: String = dir
|
||||||
|
.file_name()
|
||||||
.expect(&format!("Invalid repository path: {}", dir.display()))
|
.expect(&format!("Invalid repository path: {}", dir.display()))
|
||||||
.to_string_lossy().to_string();
|
.to_string_lossy()
|
||||||
|
.to_string();
|
||||||
let clone_url = match settings.clone_url.as_ref() {
|
let clone_url = match settings.clone_url.as_ref() {
|
||||||
Some(url) => Some(url.replace("%REPO%", &name)),
|
Some(url) => Some(url.replace("%REPO%", &name)),
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|||||||
+7
-9
@@ -106,12 +106,9 @@ impl Filter for MaskFilter {
|
|||||||
let mask: u64 = match mask.starts_with("0x") {
|
let mask: u64 = match mask.starts_with("0x") {
|
||||||
true => {
|
true => {
|
||||||
let hexstr = mask.strip_prefix("0x").unwrap();
|
let hexstr = mask.strip_prefix("0x").unwrap();
|
||||||
u64::from_str_radix(hexstr, 16)
|
u64::from_str_radix(hexstr, 16).expect("ERROR: Tera `mask` parameter is invalid hex.")
|
||||||
.expect("ERROR: Tera `mask` parameter is invalid hex.")
|
}
|
||||||
},
|
false => str::parse::<u64>(&mask).expect("ERROR: Tera `mask` parameter is not valid."),
|
||||||
false => {
|
|
||||||
str::parse::<u64>(&mask).expect("ERROR: Tera `mask` parameter is not valid.")
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
Ok(to_value(v & mask).unwrap())
|
Ok(to_value(v & mask).unwrap())
|
||||||
}
|
}
|
||||||
@@ -191,9 +188,10 @@ pub struct Pagination {
|
|||||||
}
|
}
|
||||||
impl Pagination {
|
impl Pagination {
|
||||||
pub fn new<P: AsRef<Path>>(cur: usize, total: usize, url_template: &P) -> Self {
|
pub fn new<P: AsRef<Path>>(cur: usize, total: usize, url_template: &P) -> Self {
|
||||||
let url_template = url_template.as_ref().to_str()
|
let url_template = url_template.as_ref().to_str().expect(&format!(
|
||||||
.expect(&format!("ERROR: attempted to paginate unparseable path: {}",
|
"ERROR: attempted to paginate unparseable path: {}",
|
||||||
url_template.as_ref().display()));
|
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),
|
||||||
|
|||||||
+1
-4
@@ -160,13 +160,10 @@ pub fn sanitize_path_component(var: &str) -> String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn urlify_path(path: &str) -> String {
|
pub fn urlify_path(path: &str) -> String {
|
||||||
let path = path
|
let path = path.replace("/", "+").replace("\\", "+");
|
||||||
.replace("/", "+")
|
|
||||||
.replace("\\", "+");
|
|
||||||
path.trim().to_string()
|
path.trim().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub trait SafePathVar {
|
pub trait SafePathVar {
|
||||||
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf;
|
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user