git::{dir_listing, parse_repo, GitFile, GitRepo, GitsyMetadata},
loud, louder, loudest, normal, normal_noln,
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},
};
+use chrono::{DateTime, Local};
use git2::{Error, Repository};
use rayon::prelude::*;
-use chrono::{DateTime, Local};
use std::cmp;
use std::fs::File;
use std::io::Write;
_ => Context::new(),
};
if let Some(extra) = &self.settings.extra {
- ctx
- .try_insert("extra", extra)
+ ctx.try_insert("extra", extra)
.expect("Failed to add extra settings to template engine.");
}
if let Some(site_name) = &self.settings.site_name {
}
ctx.insert("site_dir", &self.settings.outputs.output_dir());
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_offset", &self.generated_dt.offset().local_minus_utc());
let repo_path = match &repo_desc.path {
url if url.starts_with("https://") || url.to_str().unwrap_or_default().contains("@") => {
if self.settings.outputs.cloned_repos.is_none() {
- return Err(GitsyError::kind(GitsyErrorKind::Settings,
- Some(&format!("ERROR: Found remote repo [{}], but `cloned_repos` directory not configured.", name))));
+ return Err(GitsyError::kind(
+ 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]
.iter()
name,
dir.display()
);
- return Err(GitsyError::kind(GitsyErrorKind::Settings,
- Some(&format!("ERROR: Local repository not found: {}", name))));
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some(&format!("ERROR: Local repository not found: {}", name)),
+ ));
}
}
dir.to_string_lossy().to_string()
fn write_rendered<P: AsRef<Path>>(&self, path: &P, rendered: &str) -> usize {
let path: &Path = path.as_ref();
- assert!(self.settings.outputs.assert_valid(&path),
- "ERROR: attempted to write invalid path: {}", path.display());
+ assert!(
+ self.settings.outputs.assert_valid(&path),
+ "ERROR: attempted to write invalid path: {}",
+ path.display()
+ );
// Write the file to disk
let mut file = File::create(path).expect(&format!("Unable to write to output path: {}", path.display()));
file.write(rendered.as_bytes())
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
let mut global_bytes = 0;
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
Ok(rendered) => {
global_bytes += self.write_rendered(&out_path, &rendered);
let tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
let mut global_bytes = 0;
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
Ok(rendered) => {
global_bytes += self.write_rendered(&out_path, &rendered);
Ok(global_bytes)
}
- pub fn gen_summary(&self, ctx: &Context, atomic_bytes: &AtomicUsize,
- parsed_repo: &GitRepo, repo_desc: &GitsySettingsRepo, _repo: &Repository) -> Result<usize, GitsyError> {
+ pub fn gen_summary(
+ &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 mut repo_bytes = 0;
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
Ok(rendered) => {
let bytes = self.write_rendered(&out_path, &rendered);
_ => error!("ERROR: {:?}", x),
},
}
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
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 repo_bytes = AtomicUsize::new(0);
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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 page_count = pages.len();
- parsed_repo.history.par_chunks(self.settings.paginate_history()).enumerate().try_for_each(|(idx, page)| {
- let mut paged_ctx = ctx.clone();
- let pagination = Pagination::new(
- idx + 1,
- page_count,
- &out_path,
- );
- paged_ctx.insert("page", &pagination.with_relative_paths());
- paged_ctx.insert("history", &page);
- let rendered = tera.render(templ_path, &paged_ctx)?;
- let bytes = self.write_rendered(&pagination.cur_page, &rendered);
- repo_bytes.fetch_add(bytes, Ordering::SeqCst);
- atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
- paged_ctx.remove("page");
- paged_ctx.remove("history");
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
- Ok::<(), GitsyError>(())
- })?;
+ parsed_repo
+ .history
+ .par_chunks(self.settings.paginate_history())
+ .enumerate()
+ .try_for_each(|(idx, page)| {
+ let mut paged_ctx = ctx.clone();
+ let pagination = Pagination::new(idx + 1, page_count, &out_path);
+ paged_ctx.insert("page", &pagination.with_relative_paths());
+ paged_ctx.insert("history", &page);
+ let rendered = tera.render(templ_path, &paged_ctx)?;
+ let bytes = self.write_rendered(&pagination.cur_page, &rendered);
+ repo_bytes.fetch_add(bytes, Ordering::SeqCst);
+ atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
+ paged_ctx.remove("page");
+ paged_ctx.remove("history");
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
+ Ok::<(), GitsyError>(())
+ })?;
}
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 tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
let mut repo_bytes = 0;
for (_id, commit) in &parsed_repo.commits {
- ctx
- .try_insert("commit", &commit)
+ ctx.try_insert("commit", &commit)
.expect("Failed to add commit to template engine.");
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
Ok(rendered) => {
let bytes = self.write_rendered(&out_path, &rendered);
}
}
ctx.remove("commit");
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
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 mut repo_bytes = 0;
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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();
paged_ctx.remove("branches");
let pages = parsed_repo.branches.chunks(self.settings.paginate_branches());
let page_count = pages.len();
for (idx, page) in pages.enumerate() {
- let pagination = Pagination::new(
- idx + 1,
- page_count,
- &out_path,
- );
+ let pagination = Pagination::new(idx + 1, page_count, &out_path);
paged_ctx.insert("page", &pagination.with_relative_paths());
paged_ctx.insert("branches", &page);
match tera.render(templ_path, &paged_ctx) {
paged_ctx.remove("page");
paged_ctx.remove("branches");
}
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
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 tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
let mut repo_bytes = 0;
for branch in &parsed_repo.branches {
ctx.insert("branch", 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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
Ok(rendered) => {
- let bytes = self
- .write_rendered(&out_path, &rendered);
+ let bytes = self.write_rendered(&out_path, &rendered);
repo_bytes += bytes;
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
}
}
}
ctx.remove("branch");
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
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 mut repo_bytes = 0;
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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();
paged_ctx.remove("tags");
let pages = parsed_repo.tags.chunks(self.settings.paginate_tags());
let page_count = pages.len();
for (idx, page) in pages.enumerate() {
- let pagination =
- Pagination::new(idx + 1, page_count, &out_path);
+ let pagination = Pagination::new(idx + 1, page_count, &out_path);
paged_ctx.insert("page", &pagination.with_relative_paths());
paged_ctx.insert("tags", &page);
match tera.render(templ_path, &paged_ctx) {
}
paged_ctx.remove("page");
paged_ctx.remove("tags");
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
}
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 tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
let mut repo_bytes = 0;
}
}
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
Ok(rendered) => {
- let bytes =
- self.write_rendered(&out_path, &rendered);
+ let bytes = self.write_rendered(&out_path, &rendered);
repo_bytes += bytes;
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
}
}
ctx.remove("tag");
ctx.remove("commit");
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
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 tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
let mut repo_bytes = 0;
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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("all_files", &parsed_repo.all_files);
match tera.render(templ_path, &ctx) {
Ok(rendered) => {
- let bytes =
- self.write_rendered(&out_path, &rendered);
+ let bytes = self.write_rendered(&out_path, &rendered);
repo_bytes += bytes;
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
}
_ => error!("ERROR: {:?}", x),
},
}
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
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 mut repo_bytes = 0;
.expect("Invalid syntax highlighting theme specified.");
let css: String = css_for_theme_with_class_style(theme, syntect::html::ClassStyle::Spaced)
.expect("Invalid syntax highlighting theme specified.");
- let bytes =
- self.write_rendered(&self.settings.outputs.syntax_css::<GitFile>(Some(&parsed_repo), None), css.as_str());
+ let bytes = self.write_rendered(
+ &self.settings.outputs.syntax_css::<GitFile>(Some(&parsed_repo), None),
+ css.as_str(),
+ );
repo_bytes += bytes;
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
}
// done first because syntax highlighting is very slow.
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 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
.par_iter()
.fold(
let mut local_bytes = 0;
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) {
- true => GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc)
- .expect("Failed to parse file."),
+ true => {
+ GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc).expect("Failed to parse file.")
+ }
false => (*file).clone(),
};
- ctx
- .try_insert("file", &file)
+ ctx.try_insert("file", &file)
.expect("Failed to add file to template engine.");
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
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_bytes.fetch_add(local_bytes, Ordering::SeqCst);
}
.while_some() // allow short-circuiting if size limit is reached
.sum::<usize>();
repo_bytes = atomic_repo_bytes.load(Ordering::SeqCst);
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
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 tera = self.tera.as_ref().expect("ERROR: generate called without a context!?");
let mut repo_bytes = 0;
for dir in parsed_repo.all_files.iter().filter(|x| x.kind == "dir") {
let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
ctx.insert("dir", dir);
- ctx
- .try_insert("files", &listing)
+ ctx.try_insert("files", &listing)
.expect("Failed to add dir to template engine.");
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 out_path = out_path.to_str().expect(&format!("ERROR: a summary output path is invalid: {}", out_path.display()));
+ let templ_path = templ_path.to_str().expect(&format!(
+ "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) {
Ok(rendered) => {
- let bytes =
- self.write_rendered(&out_path, &rendered);
+ let bytes = self.write_rendered(&out_path, &rendered);
repo_bytes += bytes;
atomic_bytes.fetch_add(bytes, Ordering::SeqCst);
}
}
ctx.remove("files");
ctx.remove("dir");
- size_check_atomic!(repo_desc, atomic_bytes, self.total_bytes,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check_atomic!(
+ repo_desc,
+ atomic_bytes,
+ self.total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: size limit exceeded")
+ ))
+ );
}
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;
match repo_desc {
Some(repo_desc) => {
loud!(" - copied asset: {}", src_file.display());
}
}
- },
+ }
_ => {
if self.settings.asset_files.is_some() {
let target_dir = self.settings.outputs.global_assets::<GitFile>(None, None);
loud!(" - copied asset: {}", src_file.display());
}
}
- },
+ }
}
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);
let start_repo = Instant::now();
for readme in readmes {
if let Some(file) = parsed_repo.root_files.iter().filter(|x| &x.name == readme).next() {
louder!(" - found readme file: {}", file.name);
- let _ = GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc)
- .expect("Failed to parse file.");
+ let _ =
+ GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc).expect("Failed to parse file.");
local_ctx.insert("readme", &file);
break;
}
}
};
- let fns = &[GitsyGenerator::gen_summary,
- GitsyGenerator::gen_branches,
- GitsyGenerator::gen_branch,
- GitsyGenerator::gen_tags,
- GitsyGenerator::gen_tag,
- GitsyGenerator::gen_history,
- GitsyGenerator::gen_commit,
- GitsyGenerator::gen_file,
- GitsyGenerator::gen_dir,
- GitsyGenerator::gen_files,
+ let fns = &[
+ GitsyGenerator::gen_summary,
+ GitsyGenerator::gen_branches,
+ GitsyGenerator::gen_branch,
+ GitsyGenerator::gen_tags,
+ GitsyGenerator::gen_tag,
+ GitsyGenerator::gen_history,
+ GitsyGenerator::gen_commit,
+ GitsyGenerator::gen_file,
+ GitsyGenerator::gen_dir,
+ GitsyGenerator::gen_files,
];
- let repo_bytes: usize = fns.par_iter().try_fold(
- || 0,
- |acc, x| {
- let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
- let bytes = x(&self, &local_ctx, &atomic_bytes, &parsed_repo, repo_desc, &repo)?;
- // remove these bytes from the current repo bytes and move them to the total bytes.
- atomic_bytes.fetch_sub(bytes, Ordering::SeqCst);
- self.total_bytes.fetch_add(bytes, Ordering::SeqCst);
- Ok::<usize, GitsyError>(acc + bytes)
- })
+ let repo_bytes: usize = fns
+ .par_iter()
+ .try_fold(
+ || 0,
+ |acc, x| {
+ let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
+ let bytes = x(&self, &local_ctx, &atomic_bytes, &parsed_repo, repo_desc, &repo)?;
+ // remove these bytes from the current repo bytes and move them to the total bytes.
+ atomic_bytes.fetch_sub(bytes, Ordering::SeqCst);
+ self.total_bytes.fetch_add(bytes, Ordering::SeqCst);
+ Ok::<usize, GitsyError>(acc + bytes)
+ },
+ )
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
- size_check!(repo_desc, 0, self.total_bytes.load(Ordering::SeqCst),
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: size limit exceeded"))));
+ size_check!(
+ 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))?;
let mut tb = 0;
for repo_desc in &repo_vec {
let (minimized_repo, repo_bytes) = self.generate_repo(repo_desc, longest_repo_name)?;
- size_check!(repo_desc, 0, tb,
- return Err(GitsyError::kind(GitsyErrorKind::Settings, Some("ERROR: site size limit exceeded"))));
+ size_check!(
+ repo_desc,
+ 0,
+ tb,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Settings,
+ Some("ERROR: site size limit exceeded")
+ ))
+ );
shared_repos.lock().unwrap().push(minimized_repo);
tb += repo_bytes;
}
tb
- },
+ }
n if n == 0 => {
- 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)?;
- size_check!(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);
- Ok::<usize, GitsyError>(repo_bytes)
- })
+ 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)?;
+ size_check!(
+ 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);
+ Ok::<usize, GitsyError>(repo_bytes)
+ },
+ )
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
total_bytes
- },
+ }
n => {
- let pool = rayon::ThreadPoolBuilder::new()
- .num_threads(n)
- .build().unwrap();
+ let pool = rayon::ThreadPoolBuilder::new().num_threads(n).build().unwrap();
let total_bytes = pool.install(|| {
- 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)?;
- size_check!(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);
- Ok::<usize, GitsyError>(repo_bytes)
- })
+ 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)?;
+ size_check!(
+ 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);
+ Ok::<usize, GitsyError>(repo_bytes)
+ },
+ )
.try_reduce(|| 0, |acc, x| Ok(acc + x))?;
Ok::<usize, GitsyError>(total_bytes)
})?;
total_bytes
}
};
- size_check!(self.settings, 0, total_bytes, return Err(GitsyError::kind(GitsyErrorKind::Unknown,
- Some("ERROR: site size limit exceeded"))));
+ size_check!(
+ self.settings,
+ 0,
+ total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Unknown,
+ Some("ERROR: site size limit exceeded")
+ ))
+ );
let repos = shared_repos;
self.copy_assets(None, None, None)?;
total_bytes += global_bytes;
- size_check!(self.settings, 0, total_bytes, return Err(GitsyError::kind(GitsyErrorKind::Unknown,
- Some("ERROR: site size limit exceeded"))));
+ size_check!(
+ self.settings,
+ 0,
+ total_bytes,
+ return Err(GitsyError::kind(
+ GitsyErrorKind::Unknown,
+ Some("ERROR: site size limit exceeded")
+ ))
+ );
normal!(
"done in {:.2}s ({} bytes)",
start_global.elapsed().as_secs_f32(),
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
*/
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 git2::{DiffOptions, Error, Repository};
use rayon::prelude::*;
use serde::{Deserialize, Serialize};
-use std::sync::atomic::AtomicUsize;
use std::cell::RefCell;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::rc::Rc;
+use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
fn first_line(msg: &[u8]) -> String {
let new_history: Vec<GitObject> = self.history.iter().cloned().take(max_entries).collect();
for entry in &new_history {
if self.commits.contains_key(&entry.full_hash) {
- new_commits.insert(entry.full_hash.clone(),
- self.commits.get(&entry.full_hash).unwrap().clone());
+ new_commits.insert(
+ 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 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());
+ assert!(
+ src.components().count() == dst.components().count(),
+ "ERROR: path substitution accidentally created a new folder in: {}",
+ src.display()
+ );
dst
}
}
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()
+ let safe_ref = self
+ .ref_name
+ .as_deref()
.map(|v| sanitize_path_component(&urlify_path(v)))
.unwrap_or("%REF%".to_string());
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("%REF%", &safe_ref);
dst.push(cmp);
}
- assert!(src.components().count() == dst.components().count(),
- "ERROR: path substitution accidentally created a new folder in: {}", src.display());
+ assert!(
+ src.components().count() == dst.components().count(),
+ "ERROR: path substitution accidentally created a new folder in: {}",
+ src.display()
+ );
dst
}
}
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()
+ 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());
+ assert!(
+ src.components().count() == dst.components().count(),
+ "ERROR: path substitution accidentally created a new folder in: {}",
+ src.display()
+ );
dst
}
}
pub fn dir_listing(repo: &Repository, file: &GitFile) -> Result<Vec<GitFile>, Error> {
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)
}
-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![];
for (idx, oid) in revwalk.by_ref().enumerate() {
let oid = oid?;
- if idx >= settings.limit_history.unwrap_or(usize::MAX) {
+ if idx >= settings.limit_history.unwrap_or(usize::MAX) {
break;
}
let parsed = parse_commit(idx, settings, repo, &oid.to_string(), &references)?;
- loudest!(" + [{}] {} {}", idx, parsed.full_hash,
- parsed.summary.as_deref().unwrap_or_default());
+ loudest!(
+ " + [{}] {} {}",
+ idx,
+ parsed.full_hash,
+ parsed.summary.as_deref().unwrap_or_default()
+ );
history.push(parsed);
}
Ok(history)
// Let's arbitrarily say it's not worth parallelizing unless we
// can give all cores at least 1k commits to parse. This could
// certainly use some configurability...
- let thread_jobs = match rayon::current_num_threads() > 1 &&
- commit_count > 1000 * rayon::current_num_threads() {
+ let thread_jobs = match rayon::current_num_threads() > 1 && commit_count > 1000 * rayon::current_num_threads() {
// Divide a chunk up into even smaller units, so each core
// runs about 10. This makes it more efficient to detect when
// the commit limit is reached and short-circuit.
// might not be evenly distributed.
let chunk_size = ((commit_count as f64) / (thread_jobs as f64)).ceil() as usize;
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 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 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) {
- // TODO: should convert all error paths in this function
- // to GitsyErrors, and differentiate between real failures
- // and soft limits. For now, they're all stop processing,
- // but don't raise any errors. Here, we take advantage of
- // that.
- return Err(git2::Error::from_str("history limit reached"));
- }
- let repo = Repository::open(repo_path)?;
- let mut revwalk = repo.revwalk()?;
- // TODO: TOPOLOGICAL might be better, but it's also ungodly slow
- // on large repos. Maybe this should be configurable.
- //
- //revwalk.set_sorting(git2::Sort::TOPOLOGICAL)?;
- revwalk.set_sorting(git2::Sort::NONE)?;
- let start_commit = match (chunk_size * thread) + 1 > commit_count {
- true => 1,
- false => commit_count - 1 - (chunk_size * thread),
- };
- let end_commit = match chunk_size > start_commit {
- true => "".into(),
- false => format!("~{}", start_commit - chunk_size),
- };
- let range = format!("{}~{}..{}{}",
- branch_name, start_commit,
- branch_name, end_commit);
- loud!(" - Parse range: {} on thread {}", range, thread);
- match *thread == 0 {
- true => {
- // The last chunk gets a single ref instead of a
- // range, because ranges can't seem to represent the
- // very first commit in a repository...
- let end_commit = format!("{}{}", branch_name, end_commit);
- let branch_obj = repo.revparse_single(&end_commit).unwrap();
- revwalk.push(branch_obj.id())?
+ 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) {
+ // TODO: should convert all error paths in this function
+ // to GitsyErrors, and differentiate between real failures
+ // and soft limits. For now, they're all stop processing,
+ // but don't raise any errors. Here, we take advantage of
+ // that.
+ return Err(git2::Error::from_str("history limit reached"));
+ }
+ let repo = Repository::open(repo_path)?;
+ let mut revwalk = repo.revwalk()?;
+ // TODO: TOPOLOGICAL might be better, but it's also ungodly slow
+ // on large repos. Maybe this should be configurable.
+ //
+ //revwalk.set_sorting(git2::Sort::TOPOLOGICAL)?;
+ revwalk.set_sorting(git2::Sort::NONE)?;
+ let start_commit = match (chunk_size * thread) + 1 > commit_count {
+ true => 1,
+ false => commit_count - 1 - (chunk_size * thread),
+ };
+ let end_commit = match chunk_size > start_commit {
+ true => "".into(),
+ false => format!("~{}", start_commit - chunk_size),
+ };
+ let range = format!("{}~{}..{}{}", branch_name, start_commit, branch_name, end_commit);
+ loud!(" - Parse range: {} on thread {}", range, thread);
+ match *thread == 0 {
+ true => {
+ // The last chunk gets a single ref instead of a
+ // range, because ranges can't seem to represent the
+ // very first commit in a repository...
+ let end_commit = format!("{}{}", branch_name, end_commit);
+ let branch_obj = repo.revparse_single(&end_commit).unwrap();
+ revwalk.push(branch_obj.id())?
+ }
+ false => revwalk.push_range(&range)?,
+ }
+ let res = parse_revwalk(&repo, revwalk, &references, &settings)?;
+ louder!(" - Parsed {} on thread {}", res.len(), thread);
+ atomic_commits.fetch_add(res.len(), Ordering::SeqCst);
+ acc.extend(res);
+ Ok(acc)
},
- false => revwalk.push_range(&range)?,
- }
- let res = parse_revwalk(&repo, revwalk, &references, &settings)?;
- louder!(" - Parsed {} on thread {}", res.len(), thread);
- atomic_commits.fetch_add(res.len(), Ordering::SeqCst);
- acc.extend(res);
- Ok(acc)
- })
+ )
.map(|x: Result<Vec<GitObject>, Error>| x.ok())
.while_some()
.flatten_iter() // concatenate all of the vecs in series
})
}
-pub fn parse_commit(idx: usize, settings: &GitsySettingsRepo,
- repo: &Repository, refr: &str,
- references: &BTreeMap<String, Vec<String>>) -> Result<GitObject, Error> {
+pub fn parse_commit(
+ idx: usize,
+ settings: &GitsySettingsRepo,
+ repo: &Repository,
+ refr: &str,
+ references: &BTreeMap<String, Vec<String>>,
+) -> Result<GitObject, Error> {
let obj = repo.revparse_single(refr)?;
let commit = repo.find_commit(obj.id())?;
};
let (stats, commit_diff) = match idx < settings.limit_diffs.unwrap_or(usize::MAX) {
- false => {
- (None, None)
- },
+ false => (None, None),
true => {
let b = commit.tree()?;
let mut diffopts = DiffOptions::new();
let diff = repo.diff_tree_to_tree(a.as_ref(), Some(&b), Some(&mut diffopts))?;
let stats = diff.stats()?;
let commit_diff: Option<GitDiffCommit> = match idx < settings.limit_diffs.unwrap_or(usize::MAX) {
- true => {
- Some(GitDiffCommit {
- file_count: stats.files_changed(),
- additions: stats.insertions(),
- deletions: stats.deletions(),
- ..Default::default()
- })
- },
- false => {
- None
- }
+ true => Some(GitDiffCommit {
+ file_count: stats.files_changed(),
+ additions: stats.insertions(),
+ deletions: stats.deletions(),
+ ..Default::default()
+ }),
+ false => None,
};
let stats = GitStats {
files: stats.files_changed(),
None, // TODO: handle binary files?
Some(&mut |_file, hunk| {
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();
hunk_diff.context = String::from_utf8_lossy(hunk.header()).to_string();
file_diff.hunks.push(hunk_diff);
}),
Some(&mut |_file, _hunk, line| {
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
.hunks
.last_mut()
}
};
(Some(stats), commit_diff)
- },
+ }
};
let tree = obj.peel_to_tree()?;
* You should have received a copy of the GNU General Public License
* along with Itsy-Gitsy. If not, see <http://www.gnu.org/licenses/>.
*/
-use crate::{louder, error};
use crate::git::GitRepo;
use crate::util::SafePathVar;
+use crate::{error, louder};
use clap::Parser;
use git2::Repository;
use serde::Deserialize;
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::path::{Path, PathBuf};
use std::sync::atomic::Ordering;
error,
}
-pub fn substitute_path_vars<P,S>(path: &P, repo: Option<&GitRepo>, obj: Option<&S>) -> PathBuf
-where P: AsRef<Path>,
- S: SafePathVar {
+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());
+ 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
let new_path = substitute_path_vars(&tmpl_path, repo, obj);
self.canonicalize_and_create(&new_path, $is_dir)
}
- }
+ };
}
macro_rules! templates_fn {
($var:ident, $is_dir:expr) => {
pub fn $var<S: SafePathVar>(&self, repo: Option<&GitRepo>, obj: Option<&S>) -> Vec<(PathBuf, PathBuf)> {
match &self.templates {
- Some(template) => {
- template.iter()
- .filter(|x| x.kind == GitsyTemplateType::$var)
- .map(|x| {
- let tmpl_path = PathBuf::from(&x.output);
- let new_path = substitute_path_vars(&tmpl_path, repo, obj);
- (PathBuf::from(&x.template),
- self.canonicalize_and_create(&new_path, $is_dir))
- }).collect()
- },
+ Some(template) => template
+ .iter()
+ .filter(|x| x.kind == GitsyTemplateType::$var)
+ .map(|x| {
+ let tmpl_path = PathBuf::from(&x.output);
+ let new_path = substitute_path_vars(&tmpl_path, repo, obj);
+ (
+ PathBuf::from(&x.template),
+ self.canonicalize_and_create(&new_path, $is_dir),
+ )
+ })
+ .collect(),
None => {
- vec!()
- },
+ vec![]
+ }
}
}
- }
+ };
}
impl SafePathVar for GitsySettingsOutputs {
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf {
let src: &Path = path.as_ref();
let mut dst = PathBuf::new();
- let root = self.template_root.to_str()
- .expect(&format!("ERROR: couldn't parse template root: {}", self.template_root.display()));
+ let root = self.template_root.to_str().expect(&format!(
+ "ERROR: couldn't parse template root: {}",
+ self.template_root.display()
+ ));
for cmp in src.components() {
// NOTE: this variable is not sanitized, since it's
// allowed to create new directory structure.
- let cmp = cmp.as_os_str().to_string_lossy()
- .replace("%TEMPLATE%", &root);
+ let cmp = cmp.as_os_str().to_string_lossy().replace("%TEMPLATE%", &root);
dst.push(cmp);
}
dst
if cli.repos.len() > 0 {
repo_descriptions.clear();
for dir in &cli.repos {
- let name: String = dir.file_name()
+ let name: String = dir
+ .file_name()
.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() {
Some(url) => Some(url.replace("%REPO%", &name)),
_ => None,
.expect("ERROR: Tera mask filter called without `mask` parameter.")
.clone(),
)
- .expect("ERROR: Tera `mask` parameter is not valid.");
+ .expect("ERROR: Tera `mask` parameter is not valid.");
let mask: u64 = match mask.starts_with("0x") {
true => {
let hexstr = mask.strip_prefix("0x").unwrap();
- u64::from_str_radix(hexstr, 16)
- .expect("ERROR: Tera `mask` parameter is invalid hex.")
- },
- false => {
- str::parse::<u64>(&mask).expect("ERROR: Tera `mask` parameter is not valid.")
- },
+ u64::from_str_radix(hexstr, 16).expect("ERROR: Tera `mask` parameter is invalid hex.")
+ }
+ false => str::parse::<u64>(&mask).expect("ERROR: Tera `mask` parameter is not valid."),
};
Ok(to_value(v & mask).unwrap())
}
}
impl Pagination {
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 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 next = match cur + 1 <= total {
true => Some(cur + 1),
}
pub fn urlify_path(path: &str) -> String {
- let path = path
- .replace("/", "+")
- .replace("\\", "+");
+ let path = path.replace("/", "+").replace("\\", "+");
path.trim().to_string()
}
-
pub trait SafePathVar {
fn safe_substitute(&self, path: &impl AsRef<Path>) -> PathBuf;
}