apply rustfmt. ugh.
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
max_width = 120
|
||||||
+253
-175
@@ -1,30 +1,15 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
error, normal, normal_noln, loud, louder, loudest,
|
error,
|
||||||
git::{
|
git::{dir_listing, parse_repo, GitFile, GitRepo, GitsyMetadata},
|
||||||
dir_listing,
|
loud, louder, loudest, normal, normal_noln,
|
||||||
GitFile,
|
settings::{GitsyCli, GitsyRepoDescriptions, GitsySettings, GitsySettingsRepo},
|
||||||
GitRepo,
|
template::{DirFilter, FileFilter, TsDateFn, TsTimestampFn},
|
||||||
GitsyMetadata,
|
|
||||||
parse_repo,
|
|
||||||
},
|
|
||||||
settings::{
|
|
||||||
GitsyCli,
|
|
||||||
GitsyRepoDescriptions,
|
|
||||||
GitsySettings,
|
|
||||||
GitsySettingsRepo,
|
|
||||||
},
|
|
||||||
template::{
|
|
||||||
DirFilter,
|
|
||||||
FileFilter,
|
|
||||||
TsDateFn,
|
|
||||||
TsTimestampFn,
|
|
||||||
},
|
|
||||||
util::GitsyError,
|
util::GitsyError,
|
||||||
};
|
};
|
||||||
use git2::{Repository, Error};
|
use git2::{Error, Repository};
|
||||||
use rayon::prelude::*;
|
use rayon::prelude::*;
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::fs::{File, create_dir};
|
use std::fs::{create_dir, File};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
@@ -36,9 +21,9 @@ use pulldown_cmark::{html, Options, Parser as MdParser};
|
|||||||
|
|
||||||
#[cfg(any(feature = "highlight", feature = "highlight_fast"))]
|
#[cfg(any(feature = "highlight", feature = "highlight_fast"))]
|
||||||
use syntect::{
|
use syntect::{
|
||||||
html::{ClassedHTMLGenerator, ClassStyle, css_for_theme_with_class_style},
|
|
||||||
parsing::SyntaxSet,
|
|
||||||
highlighting::ThemeSet,
|
highlighting::ThemeSet,
|
||||||
|
html::{css_for_theme_with_class_style, ClassStyle, ClassedHTMLGenerator},
|
||||||
|
parsing::SyntaxSet,
|
||||||
util::LinesWithEndings,
|
util::LinesWithEndings,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,7 +37,7 @@ macro_rules! size_check {
|
|||||||
if total.saturating_add($cur) > $settings.limit_total_size.unwrap_or(usize::MAX) {
|
if total.saturating_add($cur) > $settings.limit_total_size.unwrap_or(usize::MAX) {
|
||||||
$action;
|
$action;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct GitsyGenerator {
|
pub struct GitsyGenerator {
|
||||||
@@ -62,8 +47,7 @@ pub struct GitsyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl GitsyGenerator {
|
impl GitsyGenerator {
|
||||||
pub fn new(cli: GitsyCli, settings: GitsySettings,
|
pub fn new(cli: GitsyCli, settings: GitsySettings, repo_descriptions: GitsyRepoDescriptions) -> GitsyGenerator {
|
||||||
repo_descriptions: GitsyRepoDescriptions) -> GitsyGenerator {
|
|
||||||
GitsyGenerator {
|
GitsyGenerator {
|
||||||
cli,
|
cli,
|
||||||
settings,
|
settings,
|
||||||
@@ -86,16 +70,18 @@ impl GitsyGenerator {
|
|||||||
let syntax_set = SyntaxSet::load_defaults_newlines();
|
let syntax_set = SyntaxSet::load_defaults_newlines();
|
||||||
let syntax = match syntax_set.find_syntax_by_extension(extension) {
|
let syntax = match syntax_set.find_syntax_by_extension(extension) {
|
||||||
Some(s) => s,
|
Some(s) => s,
|
||||||
_ => { return contents.to_string(); },
|
_ => {
|
||||||
|
return contents.to_string();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced);
|
let mut html_generator = ClassedHTMLGenerator::new_with_class_style(syntax, &syntax_set, ClassStyle::Spaced);
|
||||||
for line in LinesWithEndings::from(contents) {
|
for line in LinesWithEndings::from(contents) {
|
||||||
match html_generator.parse_html_for_line_which_includes_newline(line) {
|
match html_generator.parse_html_for_line_which_includes_newline(line) {
|
||||||
Ok(_) => {},
|
Ok(_) => {}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("Warning: failed to apply syntax highlighting.");
|
error!("Warning: failed to apply syntax highlighting.");
|
||||||
return contents.to_string();
|
return contents.to_string();
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
html_generator.finalize()
|
html_generator.finalize()
|
||||||
@@ -115,18 +101,22 @@ impl GitsyGenerator {
|
|||||||
loudest!(" - rendering Markdown in {}", path.display());
|
loudest!(" - rendering Markdown in {}", path.display());
|
||||||
let (cstr, rendered, pre) = (GitsyGenerator::parse_markdown(&cstr), true, false);
|
let (cstr, rendered, pre) = (GitsyGenerator::parse_markdown(&cstr), true, false);
|
||||||
(cstr, rendered, pre)
|
(cstr, rendered, pre)
|
||||||
},
|
}
|
||||||
#[cfg(any(feature = "highlight", feature = "highlight_fast"))]
|
#[cfg(any(feature = "highlight", feature = "highlight_fast"))]
|
||||||
Some(x) if settings.syntax_highlight.unwrap_or(false) => {
|
Some(x) if settings.syntax_highlight.unwrap_or(false) => {
|
||||||
loudest!(" - syntax highlighting {}", path.display());
|
loudest!(" - syntax highlighting {}", path.display());
|
||||||
(GitsyGenerator::syntax_highlight(&cstr, x.to_string_lossy().to_string().as_str()), true, true)
|
(
|
||||||
},
|
GitsyGenerator::syntax_highlight(&cstr, x.to_string_lossy().to_string().as_str()),
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
)
|
||||||
|
}
|
||||||
_ => (cstr, false, true),
|
_ => (cstr, false, true),
|
||||||
};
|
};
|
||||||
file.contents_safe = rendered;
|
file.contents_safe = rendered;
|
||||||
file.contents_preformatted = pre;
|
file.contents_preformatted = pre;
|
||||||
Some(content)
|
Some(content)
|
||||||
},
|
}
|
||||||
true => Some(format!("[Binary data ({} bytes)]", blob.content().len())),
|
true => Some(format!("[Binary data ({} bytes)]", blob.content().len())),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -134,8 +124,7 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn write_rendered(path: &str, rendered: &str) -> usize {
|
fn write_rendered(path: &str, rendered: &str) -> usize {
|
||||||
let mut file = File::create(path)
|
let mut file = File::create(path).expect(&format!("Unable to write to output path: {}", path));
|
||||||
.expect(&format!("Unable to write to output path: {}", path));
|
|
||||||
file.write(rendered.as_bytes())
|
file.write(rendered.as_bytes())
|
||||||
.expect(&format!("Failed to save rendered html to path: {}", path));
|
.expect(&format!("Failed to save rendered html to path: {}", path));
|
||||||
louder!(" - wrote file: {}", path);
|
louder!(" - wrote file: {}", path);
|
||||||
@@ -147,10 +136,10 @@ impl GitsyGenerator {
|
|||||||
template_path.push("**");
|
template_path.push("**");
|
||||||
template_path.push("*.html");
|
template_path.push("*.html");
|
||||||
let mut tera = Tera::new(&template_path.to_string_lossy().to_string())?;
|
let mut tera = Tera::new(&template_path.to_string_lossy().to_string())?;
|
||||||
tera.register_filter("only_files", FileFilter{});
|
tera.register_filter("only_files", FileFilter {});
|
||||||
tera.register_filter("only_dirs", DirFilter{});
|
tera.register_filter("only_dirs", DirFilter {});
|
||||||
tera.register_function("ts_to_date", TsDateFn{});
|
tera.register_function("ts_to_date", TsDateFn {});
|
||||||
tera.register_function("ts_to_git_timestamp", TsTimestampFn{});
|
tera.register_function("ts_to_git_timestamp", TsTimestampFn {});
|
||||||
Ok(tera)
|
Ok(tera)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,22 +153,31 @@ impl GitsyGenerator {
|
|||||||
let generated_dt = chrono::offset::Local::now();
|
let generated_dt = chrono::offset::Local::now();
|
||||||
let mut global_bytes = 0;
|
let mut global_bytes = 0;
|
||||||
let mut total_bytes = 0;
|
let mut total_bytes = 0;
|
||||||
let mut repos: Vec<GitRepo> = vec!();
|
let mut repos: Vec<GitRepo> = vec![];
|
||||||
|
|
||||||
if self.repo_descriptions.len() == 0 {
|
if self.repo_descriptions.len() == 0 {
|
||||||
panic!("No Git repositories defined! Please check your configuration file ({})",
|
panic!(
|
||||||
self.cli.path.display());
|
"No Git repositories defined! Please check your configuration file ({})",
|
||||||
|
self.cli.path.display()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the repositories by name
|
// Sort the repositories by name
|
||||||
let mut repo_vec: Vec<GitsySettingsRepo> = self.repo_descriptions.iter().cloned().collect();
|
let mut repo_vec: Vec<GitsySettingsRepo> = self.repo_descriptions.iter().cloned().collect();
|
||||||
repo_vec.sort_by(|x,y| x.name.as_deref().map(|n| n.cmp(&y.name.as_deref().unwrap_or_default()))
|
repo_vec.sort_by(|x, y| {
|
||||||
.unwrap_or(cmp::Ordering::Equal));
|
x.name
|
||||||
|
.as_deref()
|
||||||
|
.map(|n| n.cmp(&y.name.as_deref().unwrap_or_default()))
|
||||||
|
.unwrap_or(cmp::Ordering::Equal)
|
||||||
|
});
|
||||||
// Find the one with the longest name, for pretty printing
|
// Find the one with the longest name, for pretty printing
|
||||||
let global_name = "repo list";
|
let global_name = "repo list";
|
||||||
let longest_repo_name = repo_vec.iter().fold(0, |acc, x| {
|
let longest_repo_name = repo_vec
|
||||||
cmp::max(acc, x.name.as_deref().map(|n| n.len()).unwrap_or(0))
|
.iter()
|
||||||
}).max(global_name.len());
|
.fold(0, |acc, x| {
|
||||||
|
cmp::max(acc, x.name.as_deref().map(|n| n.len()).unwrap_or(0))
|
||||||
|
})
|
||||||
|
.max(global_name.len());
|
||||||
|
|
||||||
loudest!("Global settings:\n{:#?}", &self.settings);
|
loudest!("Global settings:\n{:#?}", &self.settings);
|
||||||
|
|
||||||
@@ -191,78 +189,89 @@ impl GitsyGenerator {
|
|||||||
let name = repo_desc.name.as_deref().expect("A configured repository has no name!");
|
let name = repo_desc.name.as_deref().expect("A configured repository has no name!");
|
||||||
|
|
||||||
let repo_path = match &repo_desc.path {
|
let repo_path = match &repo_desc.path {
|
||||||
url if url.starts_with("https://") ||
|
url if url.starts_with("https://") || url.to_str().unwrap_or_default().contains("@") => {
|
||||||
url.to_str().unwrap_or_default().contains("@") => {
|
if self.settings.outputs.cloned_repos.is_none() {
|
||||||
if self.settings.outputs.cloned_repos.is_none() {
|
error!(
|
||||||
error!("ERROR: Found remote repo [{}], but `cloned_repos` directory not configured.", name);
|
"ERROR: Found remote repo [{}], but `cloned_repos` directory not configured.",
|
||||||
continue;
|
name
|
||||||
};
|
);
|
||||||
let clone_path: PathBuf = [self.settings.outputs.cloned_repos.as_deref().unwrap(),
|
continue;
|
||||||
name].iter().collect();
|
};
|
||||||
match Repository::open(&clone_path) {
|
let clone_path: PathBuf = [self.settings.outputs.cloned_repos.as_deref().unwrap(), name]
|
||||||
Ok(r) => {
|
.iter()
|
||||||
// Repo already cloned, so update all refs
|
.collect();
|
||||||
let refs: Vec<String> = r.references()
|
match Repository::open(&clone_path) {
|
||||||
.expect(&format!("Unable to enumerate references for repo [{}]", name))
|
Ok(r) => {
|
||||||
.map(|x| x.expect(&format!("Found invalid reference in repo [{}]", name))
|
// Repo already cloned, so update all refs
|
||||||
.name()
|
let refs: Vec<String> = r
|
||||||
.expect(&format!("Found unnamed reference in repo: [{}]", name))
|
.references()
|
||||||
.to_string()).collect();
|
.expect(&format!("Unable to enumerate references for repo [{}]", name))
|
||||||
r.find_remote("origin")
|
.map(|x| {
|
||||||
.expect(&format!("Clone of repo [{}] missing `origin` remote.", name))
|
x.expect(&format!("Found invalid reference in repo [{}]", name))
|
||||||
.fetch(&refs, None, None)
|
.name()
|
||||||
.expect(&format!("Failed to fetch updates from remote repo [{}]", name));
|
.expect(&format!("Found unnamed reference in repo: [{}]", name))
|
||||||
clone_path.to_string_lossy().to_string()
|
.to_string()
|
||||||
},
|
})
|
||||||
Err(_) => {
|
.collect();
|
||||||
let mut builder = git2::build::RepoBuilder::new();
|
r.find_remote("origin")
|
||||||
|
.expect(&format!("Clone of repo [{}] missing `origin` remote.", name))
|
||||||
|
.fetch(&refs, None, None)
|
||||||
|
.expect(&format!("Failed to fetch updates from remote repo [{}]", name));
|
||||||
|
clone_path.to_string_lossy().to_string()
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
let mut builder = git2::build::RepoBuilder::new();
|
||||||
|
|
||||||
// TODO: git2-rs's ssh support just doesn't seem to
|
// TODO: git2-rs's ssh support just doesn't seem to
|
||||||
// work. It finds the repo, but fails to either
|
// work. It finds the repo, but fails to either
|
||||||
// decrypt or use the private key.
|
// decrypt or use the private key.
|
||||||
//
|
//
|
||||||
//if !url.starts_with("https://") {
|
//if !url.starts_with("https://") {
|
||||||
// use secrecy::ExposeSecret;
|
// use secrecy::ExposeSecret;
|
||||||
// // this must be SSH, which needs credentials.
|
// // this must be SSH, which needs credentials.
|
||||||
// let mut callbacks = git2::RemoteCallbacks::new();
|
// let mut callbacks = git2::RemoteCallbacks::new();
|
||||||
// callbacks.credentials(|_url, username_from_url, _allowed_types| {
|
// callbacks.credentials(|_url, username_from_url, _allowed_types| {
|
||||||
// //git2::Cred::ssh_key_from_agent(username_from_url.unwrap())
|
// //git2::Cred::ssh_key_from_agent(username_from_url.unwrap())
|
||||||
//
|
//
|
||||||
// let keyfile = format!("{}/.ssh/id_rsa", std::env::var("HOME").unwrap());
|
// let keyfile = format!("{}/.ssh/id_rsa", std::env::var("HOME").unwrap());
|
||||||
// let passphrase = pinentry::PassphraseInput::with_default_binary().unwrap()
|
// let passphrase = pinentry::PassphraseInput::with_default_binary().unwrap()
|
||||||
// .with_description(&format!("Enter passphrase for SSH key {} (repo: {})",
|
// .with_description(&format!("Enter passphrase for SSH key {} (repo: {})",
|
||||||
// keyfile, url.display()))
|
// keyfile, url.display()))
|
||||||
// .with_prompt("Passphrase:")
|
// .with_prompt("Passphrase:")
|
||||||
// .interact().unwrap();
|
// .interact().unwrap();
|
||||||
// git2::Cred::ssh_key(
|
// git2::Cred::ssh_key(
|
||||||
// username_from_url.unwrap(),
|
// username_from_url.unwrap(),
|
||||||
// None,
|
// None,
|
||||||
// Path::new(&keyfile),
|
// Path::new(&keyfile),
|
||||||
// Some(passphrase.expose_secret()),
|
// Some(passphrase.expose_secret()),
|
||||||
// )
|
// )
|
||||||
// });
|
// });
|
||||||
// let mut options = git2::FetchOptions::new();
|
// let mut options = git2::FetchOptions::new();
|
||||||
// options.remote_callbacks(callbacks);
|
// options.remote_callbacks(callbacks);
|
||||||
// builder.fetch_options(options);
|
// builder.fetch_options(options);
|
||||||
//}
|
//}
|
||||||
builder
|
builder
|
||||||
.bare(true)
|
.bare(true)
|
||||||
.clone(&url.to_string_lossy().to_string(), &clone_path)
|
.clone(&url.to_string_lossy().to_string(), &clone_path)
|
||||||
.expect(&format!("Failed to clone remote repo [{}]", name));
|
.expect(&format!("Failed to clone remote repo [{}]", name));
|
||||||
clone_path.to_string_lossy().to_string()
|
clone_path.to_string_lossy().to_string()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
dir => {
|
dir => {
|
||||||
match dir.metadata() {
|
match dir.metadata() {
|
||||||
Ok(m) if m.is_dir() => {},
|
Ok(m) if m.is_dir() => {}
|
||||||
_ => {
|
_ => {
|
||||||
error!("ERROR: local repository [{}]: directory not found: {}", name, dir.display());
|
error!(
|
||||||
|
"ERROR: local repository [{}]: directory not found: {}",
|
||||||
|
name,
|
||||||
|
dir.display()
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
dir.to_string_lossy().to_string()
|
dir.to_string_lossy().to_string()
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
|
let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
|
||||||
let metadata = GitsyMetadata {
|
let metadata = GitsyMetadata {
|
||||||
@@ -277,7 +286,9 @@ impl GitsyGenerator {
|
|||||||
|
|
||||||
let mut local_ctx = Context::from_serialize(&summary).unwrap();
|
let mut local_ctx = Context::from_serialize(&summary).unwrap();
|
||||||
if let Some(extra) = &self.settings.extra {
|
if let Some(extra) = &self.settings.extra {
|
||||||
local_ctx.try_insert("extra", extra).expect("Failed to add extra settings to template engine.");
|
local_ctx
|
||||||
|
.try_insert("extra", extra)
|
||||||
|
.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 {
|
||||||
local_ctx.insert("site_name", site_name);
|
local_ctx.insert("site_name", site_name);
|
||||||
@@ -294,8 +305,11 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.repo_summary.as_deref() {
|
if let Some(templ_file) = self.settings.templates.repo_summary.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.repo_summary(Some(&summary), None), &rendered);
|
repo_bytes += GitsyGenerator::write_rendered(
|
||||||
},
|
&self.settings.outputs.repo_summary(Some(&summary), None),
|
||||||
|
&rendered,
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
@@ -308,8 +322,11 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.branch.as_deref() {
|
if let Some(templ_file) = self.settings.templates.branch.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.branch(Some(&summary), Some(branch)), &rendered);
|
repo_bytes += GitsyGenerator::write_rendered(
|
||||||
},
|
&self.settings.outputs.branch(Some(&summary), Some(branch)),
|
||||||
|
&rendered,
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
@@ -329,8 +346,11 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.tag.as_deref() {
|
if let Some(templ_file) = self.settings.templates.tag.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.tag(Some(&summary), Some(tag)), &rendered);
|
repo_bytes += GitsyGenerator::write_rendered(
|
||||||
},
|
&self.settings.outputs.tag(Some(&summary), Some(tag)),
|
||||||
|
&rendered,
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
@@ -342,12 +362,17 @@ impl GitsyGenerator {
|
|||||||
|
|
||||||
for (_id, commit) in &summary.commits {
|
for (_id, commit) in &summary.commits {
|
||||||
size_check!(repo_desc, repo_bytes, total_bytes, break);
|
size_check!(repo_desc, repo_bytes, total_bytes, break);
|
||||||
local_ctx.try_insert("commit", &commit).expect("Failed to add commit to template engine.");
|
local_ctx
|
||||||
|
.try_insert("commit", &commit)
|
||||||
|
.expect("Failed to add commit to template engine.");
|
||||||
if let Some(templ_file) = self.settings.templates.commit.as_deref() {
|
if let Some(templ_file) = self.settings.templates.commit.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.commit(Some(&summary), Some(commit)), &rendered);
|
repo_bytes += GitsyGenerator::write_rendered(
|
||||||
},
|
&self.settings.outputs.commit(Some(&summary), Some(commit)),
|
||||||
|
&rendered,
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
@@ -359,44 +384,65 @@ impl GitsyGenerator {
|
|||||||
#[cfg(any(feature = "highlight", feature = "highlight_fast"))]
|
#[cfg(any(feature = "highlight", feature = "highlight_fast"))]
|
||||||
if self.settings.templates.file.is_some() {
|
if self.settings.templates.file.is_some() {
|
||||||
let ts = ThemeSet::load_defaults();
|
let ts = ThemeSet::load_defaults();
|
||||||
let theme = ts.themes.get(repo_desc.syntax_highlight_theme.as_deref()
|
let theme = ts
|
||||||
.unwrap_or("base16-ocean.light")).expect("Invalid syntax highlighting theme specified.");
|
.themes
|
||||||
|
.get(
|
||||||
|
repo_desc
|
||||||
|
.syntax_highlight_theme
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or("base16-ocean.light"),
|
||||||
|
)
|
||||||
|
.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.");
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.syntax_css(Some(&summary), None), css.as_str());
|
repo_bytes += GitsyGenerator::write_rendered(
|
||||||
|
&self.settings.outputs.syntax_css(Some(&summary), None),
|
||||||
|
css.as_str(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO: parallelize the rest of the processing steps. This one is
|
// TODO: parallelize the rest of the processing steps. This one is
|
||||||
// done first because syntax highlighting is very slow.
|
// done first because syntax highlighting is very slow.
|
||||||
let files: Vec<&GitFile> = summary.all_files.iter().filter(|x| x.kind == "file").collect();
|
let files: Vec<&GitFile> = summary.all_files.iter().filter(|x| x.kind == "file").collect();
|
||||||
let atomic_bytes: AtomicUsize = AtomicUsize::new(repo_bytes);
|
let atomic_bytes: AtomicUsize = AtomicUsize::new(repo_bytes);
|
||||||
let _ = files.par_iter().fold(|| Some(0), |acc, file| {
|
let _ = files
|
||||||
// These two have to be recreated. Cloning the Tera context is expensive.
|
.par_iter()
|
||||||
let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
|
.fold(
|
||||||
let mut local_ctx = local_ctx.clone();
|
|| Some(0),
|
||||||
|
|acc, file| {
|
||||||
|
// These two have to be recreated. Cloning the Tera context is expensive.
|
||||||
|
let repo = Repository::open(&repo_path).expect("Unable to find git repository.");
|
||||||
|
let mut local_ctx = local_ctx.clone();
|
||||||
|
|
||||||
let mut local_bytes = 0;
|
let mut local_bytes = 0;
|
||||||
let cur_repo_bytes = atomic_bytes.load(Ordering::Relaxed);
|
let cur_repo_bytes = atomic_bytes.load(Ordering::Relaxed);
|
||||||
size_check!(repo_desc, cur_repo_bytes, total_bytes, return None);
|
size_check!(repo_desc, cur_repo_bytes, total_bytes, 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).expect("Failed to parse file."),
|
true => GitsyGenerator::fill_file_contents(&repo, &file, &repo_desc)
|
||||||
false => (*file).clone(),
|
.expect("Failed to parse file."),
|
||||||
};
|
false => (*file).clone(),
|
||||||
local_ctx.try_insert("file", &file).expect("Failed to add file to template engine.");
|
};
|
||||||
if let Some(templ_file) = self.settings.templates.file.as_deref() {
|
local_ctx
|
||||||
match tera.render(templ_file, &local_ctx) {
|
.try_insert("file", &file)
|
||||||
Ok(rendered) => {
|
.expect("Failed to add file to template engine.");
|
||||||
local_bytes = GitsyGenerator::write_rendered(&self.settings.outputs.file(Some(&summary), Some(&file)), &rendered);
|
if let Some(templ_file) = self.settings.templates.file.as_deref() {
|
||||||
atomic_bytes.fetch_add(local_bytes, Ordering::Relaxed);
|
match tera.render(templ_file, &local_ctx) {
|
||||||
},
|
Ok(rendered) => {
|
||||||
Err(x) => match x.kind {
|
local_bytes = GitsyGenerator::write_rendered(
|
||||||
_ => error!("ERROR: {:?}", x),
|
&self.settings.outputs.file(Some(&summary), Some(&file)),
|
||||||
},
|
&rendered,
|
||||||
}
|
);
|
||||||
}
|
atomic_bytes.fetch_add(local_bytes, Ordering::Relaxed);
|
||||||
local_ctx.remove("file");
|
}
|
||||||
Some(acc.unwrap() + local_bytes)})
|
Err(x) => match x.kind {
|
||||||
|
_ => error!("ERROR: {:?}", x),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
local_ctx.remove("file");
|
||||||
|
Some(acc.unwrap() + local_bytes)
|
||||||
|
},
|
||||||
|
)
|
||||||
.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_bytes.load(Ordering::Relaxed);
|
repo_bytes = atomic_bytes.load(Ordering::Relaxed);
|
||||||
@@ -407,12 +453,17 @@ impl GitsyGenerator {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
|
let listing = dir_listing(&repo, &dir).expect("Failed to parse file.");
|
||||||
local_ctx.try_insert("files", &listing).expect("Failed to add dir to template engine.");
|
local_ctx
|
||||||
|
.try_insert("files", &listing)
|
||||||
|
.expect("Failed to add dir to template engine.");
|
||||||
if let Some(templ_file) = self.settings.templates.dir.as_deref() {
|
if let Some(templ_file) = self.settings.templates.dir.as_deref() {
|
||||||
match tera.render(templ_file, &local_ctx) {
|
match tera.render(templ_file, &local_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
repo_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.dir(Some(&summary), Some(dir)), &rendered);
|
repo_bytes += GitsyGenerator::write_rendered(
|
||||||
},
|
&self.settings.outputs.dir(Some(&summary), Some(dir)),
|
||||||
|
&rendered,
|
||||||
|
);
|
||||||
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
@@ -426,12 +477,16 @@ impl GitsyGenerator {
|
|||||||
for src_file in repo_desc.asset_files.as_ref().unwrap() {
|
for src_file in repo_desc.asset_files.as_ref().unwrap() {
|
||||||
let src_file = PathBuf::from(repo_path.to_owned() + "/" + src_file);
|
let src_file = PathBuf::from(repo_path.to_owned() + "/" + src_file);
|
||||||
let mut dst_file = PathBuf::from(&target_dir);
|
let mut dst_file = PathBuf::from(&target_dir);
|
||||||
dst_file.push(src_file.file_name()
|
dst_file.push(src_file.file_name().expect(&format!(
|
||||||
.expect(&format!("Failed to copy repo asset file: {} ({})",
|
"Failed to copy repo asset file: {} ({})",
|
||||||
src_file.display(), repo_desc.name.as_deref().unwrap_or_default())));
|
src_file.display(),
|
||||||
std::fs::copy(&src_file, &dst_file)
|
repo_desc.name.as_deref().unwrap_or_default()
|
||||||
.expect(&format!("Failed to copy repo asset file: {} ({})",
|
)));
|
||||||
src_file.display(), repo_desc.name.as_deref().unwrap_or_default()));
|
std::fs::copy(&src_file, &dst_file).expect(&format!(
|
||||||
|
"Failed to copy repo asset file: {} ({})",
|
||||||
|
src_file.display(),
|
||||||
|
repo_desc.name.as_deref().unwrap_or_default()
|
||||||
|
));
|
||||||
if let Ok(meta) = std::fs::metadata(dst_file) {
|
if let Ok(meta) = std::fs::metadata(dst_file) {
|
||||||
repo_bytes += meta.len() as usize;
|
repo_bytes += meta.len() as usize;
|
||||||
}
|
}
|
||||||
@@ -439,22 +494,33 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
repos.push(summary);
|
repos.push(summary);
|
||||||
normal!("{}done in {:.2}s ({} bytes)",
|
normal!(
|
||||||
match crate::util::VERBOSITY.load(Ordering::Relaxed) > 1 {
|
"{}done in {:.2}s ({} bytes)",
|
||||||
true => " - ",
|
match crate::util::VERBOSITY.load(Ordering::Relaxed) > 1 {
|
||||||
_ => "",
|
true => " - ",
|
||||||
},
|
_ => "",
|
||||||
start_repo.elapsed().as_secs_f32(), repo_bytes);
|
},
|
||||||
|
start_repo.elapsed().as_secs_f32(),
|
||||||
|
repo_bytes
|
||||||
|
);
|
||||||
total_bytes += repo_bytes;
|
total_bytes += repo_bytes;
|
||||||
size_check!(repo_desc, 0, total_bytes, break); // break if total is exceeded
|
size_check!(repo_desc, 0, total_bytes, break); // break if total is exceeded
|
||||||
}
|
}
|
||||||
|
|
||||||
let start_global = Instant::now();
|
let start_global = Instant::now();
|
||||||
normal_noln!("[{}{}]... ", global_name, " ".repeat(longest_repo_name - global_name.len()));
|
normal_noln!(
|
||||||
|
"[{}{}]... ",
|
||||||
|
global_name,
|
||||||
|
" ".repeat(longest_repo_name - global_name.len())
|
||||||
|
);
|
||||||
let mut global_ctx = Context::new();
|
let mut global_ctx = Context::new();
|
||||||
global_ctx.try_insert("repos", &repos).expect("Failed to add repo to template engine.");
|
global_ctx
|
||||||
|
.try_insert("repos", &repos)
|
||||||
|
.expect("Failed to add repo to template engine.");
|
||||||
if let Some(extra) = &self.settings.extra {
|
if let Some(extra) = &self.settings.extra {
|
||||||
global_ctx.try_insert("extra", extra).expect("Failed to add extra settings to template engine.");
|
global_ctx
|
||||||
|
.try_insert("extra", extra)
|
||||||
|
.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 {
|
||||||
global_ctx.insert("site_name", site_name);
|
global_ctx.insert("site_name", site_name);
|
||||||
@@ -471,8 +537,9 @@ impl GitsyGenerator {
|
|||||||
if let Some(templ_file) = self.settings.templates.repo_list.as_deref() {
|
if let Some(templ_file) = self.settings.templates.repo_list.as_deref() {
|
||||||
match tera.render(templ_file, &global_ctx) {
|
match tera.render(templ_file, &global_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.repo_list(None, None), &rendered);
|
global_bytes +=
|
||||||
},
|
GitsyGenerator::write_rendered(&self.settings.outputs.repo_list(None, None), &rendered);
|
||||||
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
@@ -483,7 +550,7 @@ impl GitsyGenerator {
|
|||||||
match tera.render(templ_file, &global_ctx) {
|
match tera.render(templ_file, &global_ctx) {
|
||||||
Ok(rendered) => {
|
Ok(rendered) => {
|
||||||
global_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.error(None, None), &rendered);
|
global_bytes += GitsyGenerator::write_rendered(&self.settings.outputs.error(None, None), &rendered);
|
||||||
},
|
}
|
||||||
Err(x) => match x.kind {
|
Err(x) => match x.kind {
|
||||||
_ => error!("ERROR: {:?}", x),
|
_ => error!("ERROR: {:?}", x),
|
||||||
},
|
},
|
||||||
@@ -495,8 +562,11 @@ impl GitsyGenerator {
|
|||||||
for src_file in self.settings.asset_files.as_ref().unwrap() {
|
for src_file in self.settings.asset_files.as_ref().unwrap() {
|
||||||
let src_file = PathBuf::from(src_file);
|
let src_file = PathBuf::from(src_file);
|
||||||
let mut dst_file = PathBuf::from(&target_dir);
|
let mut dst_file = PathBuf::from(&target_dir);
|
||||||
dst_file.push(src_file.file_name()
|
dst_file.push(
|
||||||
.expect(&format!("Failed to copy asset file: {}", src_file.display())));
|
src_file
|
||||||
|
.file_name()
|
||||||
|
.expect(&format!("Failed to copy asset file: {}", src_file.display())),
|
||||||
|
);
|
||||||
std::fs::copy(&src_file, &dst_file)
|
std::fs::copy(&src_file, &dst_file)
|
||||||
.expect(&format!("Failed to copy asset file: {}", src_file.display()));
|
.expect(&format!("Failed to copy asset file: {}", src_file.display()));
|
||||||
if let Ok(meta) = std::fs::metadata(dst_file) {
|
if let Ok(meta) = std::fs::metadata(dst_file) {
|
||||||
@@ -506,8 +576,16 @@ impl GitsyGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
total_bytes += global_bytes;
|
total_bytes += global_bytes;
|
||||||
normal!("done in {:.2}s ({} bytes)", start_global.elapsed().as_secs_f32(), global_bytes);
|
normal!(
|
||||||
loud!("Wrote {} bytes in {:.2}s", total_bytes, start_all.elapsed().as_secs_f32());
|
"done in {:.2}s ({} bytes)",
|
||||||
|
start_global.elapsed().as_secs_f32(),
|
||||||
|
global_bytes
|
||||||
|
);
|
||||||
|
loud!(
|
||||||
|
"Wrote {} bytes in {:.2}s",
|
||||||
|
total_bytes,
|
||||||
|
start_all.elapsed().as_secs_f32()
|
||||||
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+89
-50
@@ -1,7 +1,7 @@
|
|||||||
use crate::{error, loud, loudest};
|
|
||||||
use crate::settings::GitsySettingsRepo;
|
use crate::settings::GitsySettingsRepo;
|
||||||
use git2::{DiffOptions, Repository, Error};
|
use crate::{error, loud, loudest};
|
||||||
use serde::{Serialize, Deserialize};
|
use git2::{DiffOptions, Error, Repository};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@@ -98,7 +98,7 @@ pub struct GitDiffFile {
|
|||||||
pub extra: String,
|
pub extra: String,
|
||||||
pub additions: usize,
|
pub additions: usize,
|
||||||
pub deletions: usize,
|
pub deletions: usize,
|
||||||
pub hunks: Vec<GitDiffHunk>
|
pub hunks: Vec<GitDiffHunk>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Default)]
|
#[derive(Serialize, Default)]
|
||||||
@@ -114,8 +114,15 @@ pub struct GitDiffLine {
|
|||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn walk_file_tree(repo: &git2::Repository, rev: &str, files: &mut Vec<GitFile>,
|
fn walk_file_tree(
|
||||||
depth: usize, max_depth: usize, recurse: bool, prefix: &str) -> Result<(), Error> {
|
repo: &git2::Repository,
|
||||||
|
rev: &str,
|
||||||
|
files: &mut Vec<GitFile>,
|
||||||
|
depth: usize,
|
||||||
|
max_depth: usize,
|
||||||
|
recurse: bool,
|
||||||
|
prefix: &str,
|
||||||
|
) -> Result<(), Error> {
|
||||||
let obj = repo.revparse_single(rev)?;
|
let obj = repo.revparse_single(rev)?;
|
||||||
let tree = obj.peel_to_tree()?;
|
let tree = obj.peel_to_tree()?;
|
||||||
for entry in tree.iter() {
|
for entry in tree.iter() {
|
||||||
@@ -151,23 +158,35 @@ fn walk_file_tree(repo: &git2::Repository, rev: &str, files: &mut Vec<GitFile>,
|
|||||||
});
|
});
|
||||||
if recurse && depth < (max_depth - 1) && entry.kind() == Some(git2::ObjectType::Tree) {
|
if recurse && depth < (max_depth - 1) && entry.kind() == Some(git2::ObjectType::Tree) {
|
||||||
let prefix = path + "/";
|
let prefix = path + "/";
|
||||||
walk_file_tree(repo, &entry.id().to_string(), files,
|
walk_file_tree(
|
||||||
depth+1, max_depth, true, &prefix)?;
|
repo,
|
||||||
|
&entry.id().to_string(),
|
||||||
|
files,
|
||||||
|
depth + 1,
|
||||||
|
max_depth,
|
||||||
|
true,
|
||||||
|
&prefix,
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
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, "")?;
|
walk_file_tree(&repo, &file.id, &mut files, 0, usize::MAX, false, "")?;
|
||||||
Ok(files)
|
Ok(files)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, metadata: GitsyMetadata) -> Result<GitRepo, Error> {
|
pub fn parse_repo(
|
||||||
let mut history: Vec<GitObject> = vec!();
|
repo: &Repository,
|
||||||
let mut branches: Vec<GitObject> = vec!();
|
name: &str,
|
||||||
let mut tags: Vec<GitObject> = vec!();
|
settings: &GitsySettingsRepo,
|
||||||
|
metadata: GitsyMetadata,
|
||||||
|
) -> Result<GitRepo, Error> {
|
||||||
|
let mut history: Vec<GitObject> = vec![];
|
||||||
|
let mut branches: Vec<GitObject> = vec![];
|
||||||
|
let mut tags: Vec<GitObject> = vec![];
|
||||||
let mut commits: BTreeMap<String, GitObject> = BTreeMap::new();
|
let mut commits: BTreeMap<String, GitObject> = BTreeMap::new();
|
||||||
let mut commit_count = 0;
|
let mut commit_count = 0;
|
||||||
let mut history_count = 0;
|
let mut history_count = 0;
|
||||||
@@ -181,8 +200,12 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
if let (Some(target), Some(name)) = (refr.target(), refr.shorthand()) {
|
if let (Some(target), Some(name)) = (refr.target(), refr.shorthand()) {
|
||||||
let id = target.to_string();
|
let id = target.to_string();
|
||||||
match references.contains_key(&id) {
|
match references.contains_key(&id) {
|
||||||
false => { references.insert(target.to_string(), vec!(name.to_string())); },
|
false => {
|
||||||
true => { references.get_mut(&id).unwrap().push(name.to_string()); },
|
references.insert(target.to_string(), vec![name.to_string()]);
|
||||||
|
}
|
||||||
|
true => {
|
||||||
|
references.get_mut(&id).unwrap().push(name.to_string());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,9 +217,10 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
loudest!(" - Parsing history:");
|
loudest!(" - Parsing history:");
|
||||||
for oid in revwalk {
|
for oid in revwalk {
|
||||||
let oid = oid?;
|
let oid = oid?;
|
||||||
if commit_count >= settings.limit_commits.unwrap_or(usize::MAX) ||
|
if commit_count >= settings.limit_commits.unwrap_or(usize::MAX)
|
||||||
history_count >= settings.limit_history.unwrap_or(usize::MAX) {
|
|| history_count >= settings.limit_history.unwrap_or(usize::MAX)
|
||||||
break;
|
{
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
commits.insert(oid.to_string(), parse_commit(repo, &oid.to_string())?);
|
commits.insert(oid.to_string(), parse_commit(repo, &oid.to_string())?);
|
||||||
commit_count += 1;
|
commit_count += 1;
|
||||||
@@ -205,7 +229,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
let full_hash = commit.id().to_string();
|
let full_hash = commit.id().to_string();
|
||||||
let short_hash = obj.short_id()?.as_str().unwrap_or_default().to_string();
|
let short_hash = obj.short_id()?.as_str().unwrap_or_default().to_string();
|
||||||
|
|
||||||
let mut parents: Vec<String> = vec!();
|
let mut parents: Vec<String> = vec![];
|
||||||
let a = if commit.parents().len() == 1 {
|
let a = if commit.parents().len() == 1 {
|
||||||
let parent = commit.parent(0)?;
|
let parent = commit.parent(0)?;
|
||||||
parents.push(parent.id().to_string());
|
parents.push(parent.id().to_string());
|
||||||
@@ -223,8 +247,10 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
deletions: stats.deletions(),
|
deletions: stats.deletions(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let alt_refs: Vec<String> = references.get(&commit.id().to_string())
|
let alt_refs: Vec<String> = references
|
||||||
.map(|x| x.to_owned()).unwrap_or_default();
|
.get(&commit.id().to_string())
|
||||||
|
.map(|x| x.to_owned())
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
if history_count < settings.limit_history.unwrap_or(usize::MAX) {
|
if history_count < settings.limit_history.unwrap_or(usize::MAX) {
|
||||||
loudest!(" + {} {}", full_hash, first_line(commit.message_bytes()));
|
loudest!(" + {} {}", full_hash, first_line(commit.message_bytes()));
|
||||||
@@ -241,7 +267,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
ref_name: None,
|
ref_name: None,
|
||||||
alt_refs,
|
alt_refs,
|
||||||
author: GitAuthor {
|
author: GitAuthor {
|
||||||
name: commit.author().name().map(|x| x.to_owned()),
|
name: commit.author().name().map(|x| x.to_owned()),
|
||||||
email: commit.author().email().map(|x| x.to_owned()),
|
email: commit.author().email().map(|x| x.to_owned()),
|
||||||
},
|
},
|
||||||
summary: Some(first_line(commit.message_bytes())),
|
summary: Some(first_line(commit.message_bytes())),
|
||||||
@@ -267,7 +293,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
// this is a bad idea?
|
// this is a bad idea?
|
||||||
match refr.kind() {
|
match refr.kind() {
|
||||||
Some(k) if k == git2::ReferenceType::Symbolic => continue,
|
Some(k) if k == git2::ReferenceType::Symbolic => continue,
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
let commit = repo.find_commit(obj.id())?;
|
let commit = repo.find_commit(obj.id())?;
|
||||||
let full_hash = obj.id().to_string();
|
let full_hash = obj.id().to_string();
|
||||||
@@ -278,7 +304,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
short_hash,
|
short_hash,
|
||||||
ts_utc: commit.author().when().seconds(),
|
ts_utc: commit.author().when().seconds(),
|
||||||
ts_offset: (commit.author().when().offset_minutes() as i64) * 60,
|
ts_offset: (commit.author().when().offset_minutes() as i64) * 60,
|
||||||
parents: vec!(),
|
parents: vec![],
|
||||||
ref_name: Some(name.to_string()),
|
ref_name: Some(name.to_string()),
|
||||||
author: GitAuthor {
|
author: GitAuthor {
|
||||||
name: commit.author().name().map(|x| x.to_owned()),
|
name: commit.author().name().map(|x| x.to_owned()),
|
||||||
@@ -323,9 +349,8 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
Some(t) => (t.when().seconds(), (t.when().offset_minutes() as i64) * 60),
|
Some(t) => (t.when().seconds(), (t.when().offset_minutes() as i64) * 60),
|
||||||
_ => (0, 0),
|
_ => (0, 0),
|
||||||
};
|
};
|
||||||
let (author,email) = match commit.tagger() {
|
let (author, email) = match commit.tagger() {
|
||||||
Some(t) => (t.name().map(|x| x.to_owned()),
|
Some(t) => (t.name().map(|x| x.to_owned()), t.email().map(|x| x.to_owned())),
|
||||||
t.email().map(|x| x.to_owned())),
|
|
||||||
_ => (None, None),
|
_ => (None, None),
|
||||||
};
|
};
|
||||||
let summary = match commit.message_bytes() {
|
let summary = match commit.message_bytes() {
|
||||||
@@ -339,10 +364,7 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
ts_utc: ts,
|
ts_utc: ts,
|
||||||
ts_offset: tz,
|
ts_offset: tz,
|
||||||
ref_name: Some(tag.to_string()),
|
ref_name: Some(tag.to_string()),
|
||||||
author: GitAuthor {
|
author: GitAuthor { name: author, email },
|
||||||
name: author,
|
|
||||||
email,
|
|
||||||
},
|
|
||||||
tagged_id: Some(commit.target_id().to_string()),
|
tagged_id: Some(commit.target_id().to_string()),
|
||||||
message: commit.message().map(|x| x.to_string()),
|
message: commit.message().map(|x| x.to_string()),
|
||||||
summary,
|
summary,
|
||||||
@@ -352,8 +374,8 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
}
|
}
|
||||||
loud!(" - parsed {} tags", tag_count);
|
loud!(" - parsed {} tags", tag_count);
|
||||||
|
|
||||||
let mut root_files: Vec<GitFile> = vec!();
|
let mut root_files: Vec<GitFile> = vec![];
|
||||||
let mut all_files: Vec<GitFile> = vec!();
|
let mut all_files: Vec<GitFile> = vec![];
|
||||||
let max_depth = settings.limit_tree_depth.unwrap_or(usize::MAX);
|
let max_depth = settings.limit_tree_depth.unwrap_or(usize::MAX);
|
||||||
if max_depth > 0 {
|
if max_depth > 0 {
|
||||||
loudest!(" - Walking root files");
|
loudest!(" - Walking root files");
|
||||||
@@ -380,23 +402,21 @@ pub fn parse_repo(repo: &Repository, name: &str, settings: &GitsySettingsRepo, m
|
|||||||
pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
|
pub fn parse_commit(repo: &Repository, refr: &str) -> 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())?;
|
||||||
let mut parents: Vec<String> = vec!();
|
let mut parents: Vec<String> = vec![];
|
||||||
|
|
||||||
let a = match commit.parents().len() {
|
let a = match commit.parents().len() {
|
||||||
x if x == 1 => {
|
x if x == 1 => {
|
||||||
let parent = commit.parent(0).unwrap();
|
let parent = commit.parent(0).unwrap();
|
||||||
parents.push(parent.id().to_string());
|
parents.push(parent.id().to_string());
|
||||||
Some(parent.tree()?)
|
Some(parent.tree()?)
|
||||||
},
|
}
|
||||||
x if x > 1 => {
|
x if x > 1 => {
|
||||||
for parent in commit.parents() {
|
for parent in commit.parents() {
|
||||||
parents.push(parent.id().to_string());
|
parents.push(parent.id().to_string());
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
},
|
}
|
||||||
_ => {
|
_ => None,
|
||||||
None
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
let b = commit.tree()?;
|
let b = commit.tree()?;
|
||||||
let mut diffopts = DiffOptions::new();
|
let mut diffopts = DiffOptions::new();
|
||||||
@@ -409,22 +429,38 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
|
|||||||
deletions: stats.deletions(),
|
deletions: stats.deletions(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
let files: Rc<RefCell<Vec<GitDiffFile>>> = Rc::new(RefCell::new(vec!()));
|
let files: Rc<RefCell<Vec<GitDiffFile>>> = Rc::new(RefCell::new(vec![]));
|
||||||
|
|
||||||
diff.foreach(
|
diff.foreach(
|
||||||
&mut |file, _progress| {
|
&mut |file, _progress| {
|
||||||
let mut file_diff: GitDiffFile = Default::default();
|
let mut file_diff: GitDiffFile = Default::default();
|
||||||
file_diff.newfile = match file.status() {
|
file_diff.newfile = match file.status() {
|
||||||
git2::Delta::Deleted => "/dev/null".to_owned(),
|
git2::Delta::Deleted => "/dev/null".to_owned(),
|
||||||
_ => file.new_file().path().map(|x| "b/".to_string() + &x.to_string_lossy()).unwrap_or("/dev/null".to_string()),
|
_ => file
|
||||||
|
.new_file()
|
||||||
|
.path()
|
||||||
|
.map(|x| "b/".to_string() + &x.to_string_lossy())
|
||||||
|
.unwrap_or("/dev/null".to_string()),
|
||||||
};
|
};
|
||||||
file_diff.oldfile = match file.status() {
|
file_diff.oldfile = match file.status() {
|
||||||
git2::Delta::Added => "/dev/null".to_owned(),
|
git2::Delta::Added => "/dev/null".to_owned(),
|
||||||
_ => file.old_file().path().map(|x| "a/".to_string() + &x.to_string_lossy()).unwrap_or("/dev/null".to_string()),
|
_ => file
|
||||||
|
.old_file()
|
||||||
|
.path()
|
||||||
|
.map(|x| "a/".to_string() + &x.to_string_lossy())
|
||||||
|
.unwrap_or("/dev/null".to_string()),
|
||||||
};
|
};
|
||||||
file_diff.basefile = match file.status() {
|
file_diff.basefile = match file.status() {
|
||||||
git2::Delta::Added => file.new_file().path().map(|x| x.to_string_lossy().to_string()).unwrap_or("/dev/null".to_string()),
|
git2::Delta::Added => file
|
||||||
_ => file.old_file().path().map(|x| x.to_string_lossy().to_string()).unwrap_or("/dev/null".to_string()),
|
.new_file()
|
||||||
|
.path()
|
||||||
|
.map(|x| x.to_string_lossy().to_string())
|
||||||
|
.unwrap_or("/dev/null".to_string()),
|
||||||
|
_ => file
|
||||||
|
.old_file()
|
||||||
|
.path()
|
||||||
|
.map(|x| x.to_string_lossy().to_string())
|
||||||
|
.unwrap_or("/dev/null".to_string()),
|
||||||
};
|
};
|
||||||
file_diff.oldid = file.old_file().id().to_string();
|
file_diff.oldid = file.old_file().id().to_string();
|
||||||
file_diff.newid = file.new_file().id().to_string();
|
file_diff.newid = file.new_file().id().to_string();
|
||||||
@@ -443,7 +479,10 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
|
|||||||
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.hunks.last_mut().expect("Diff line not associated with a hunk!");
|
let hunk_diff: &mut GitDiffHunk = file_diff
|
||||||
|
.hunks
|
||||||
|
.last_mut()
|
||||||
|
.expect("Diff line not associated with a hunk!");
|
||||||
let (kind, prefix) = match line.origin() {
|
let (kind, prefix) = match line.origin() {
|
||||||
' ' => ("ctx", " "),
|
' ' => ("ctx", " "),
|
||||||
'-' => ("del", "-"),
|
'-' => ("del", "-"),
|
||||||
@@ -453,7 +492,7 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
|
|||||||
match line.origin() {
|
match line.origin() {
|
||||||
'-' => file_diff.deletions += 1,
|
'-' => file_diff.deletions += 1,
|
||||||
'+' => file_diff.additions += 1,
|
'+' => file_diff.additions += 1,
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
let line_diff = GitDiffLine {
|
let line_diff = GitDiffLine {
|
||||||
text: String::from_utf8_lossy(line.content()).to_string(),
|
text: String::from_utf8_lossy(line.content()).to_string(),
|
||||||
@@ -462,15 +501,15 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
|
|||||||
};
|
};
|
||||||
hunk_diff.lines.push(line_diff);
|
hunk_diff.lines.push(line_diff);
|
||||||
true
|
true
|
||||||
})
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
match Rc::try_unwrap(files) {
|
match Rc::try_unwrap(files) {
|
||||||
Ok(files) => {
|
Ok(files) => {
|
||||||
let files: Vec<GitDiffFile> = files.into_inner();
|
let files: Vec<GitDiffFile> = files.into_inner();
|
||||||
commit_diff.files = files;
|
commit_diff.files = files;
|
||||||
},
|
}
|
||||||
Err(_) => {},
|
Err(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
let tree = obj.peel_to_tree()?;
|
let tree = obj.peel_to_tree()?;
|
||||||
@@ -483,7 +522,7 @@ pub fn parse_commit(repo: &Repository, refr: &str) -> Result<GitObject, Error> {
|
|||||||
tree_id: Some(tree.id().to_string()),
|
tree_id: Some(tree.id().to_string()),
|
||||||
parents,
|
parents,
|
||||||
ref_name: None,
|
ref_name: None,
|
||||||
alt_refs: vec!(),
|
alt_refs: vec![],
|
||||||
author: GitAuthor {
|
author: GitAuthor {
|
||||||
name: commit.author().name().map(|x| x.to_string()),
|
name: commit.author().name().map(|x| x.to_string()),
|
||||||
email: commit.author().email().map(|x| x.to_string()),
|
email: commit.author().email().map(|x| x.to_string()),
|
||||||
|
|||||||
+52
-43
@@ -1,10 +1,6 @@
|
|||||||
use clap::Parser;
|
|
||||||
use crate::error;
|
use crate::error;
|
||||||
use crate::git::{
|
use crate::git::{GitFile, GitObject, GitRepo};
|
||||||
GitFile,
|
use clap::Parser;
|
||||||
GitObject,
|
|
||||||
GitRepo,
|
|
||||||
};
|
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||||
use std::fs::{create_dir_all, read_dir, read_to_string};
|
use std::fs::{create_dir_all, read_dir, read_to_string};
|
||||||
@@ -39,7 +35,10 @@ impl GitsyCli {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let cli = CliArgs::parse();
|
let cli = CliArgs::parse();
|
||||||
let config_path = cli.config.as_deref().unwrap_or(Path::new("config.toml")).to_owned();
|
let config_path = cli.config.as_deref().unwrap_or(Path::new("config.toml")).to_owned();
|
||||||
let config_dir = config_path.parent().expect("Config file not in valid directory.").to_owned();
|
let config_dir = config_path
|
||||||
|
.parent()
|
||||||
|
.expect("Config file not in valid directory.")
|
||||||
|
.to_owned();
|
||||||
let config_dir = match config_dir.to_str().unwrap_or_default().len() > 0 {
|
let config_dir = match config_dir.to_str().unwrap_or_default().len() > 0 {
|
||||||
true => config_dir,
|
true => config_dir,
|
||||||
false => PathBuf::from("."),
|
false => PathBuf::from("."),
|
||||||
@@ -52,10 +51,13 @@ impl GitsyCli {
|
|||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
_ => config_dir.clone(),
|
_ => config_dir.clone(),
|
||||||
};
|
};
|
||||||
crate::util::VERBOSITY.store(match cli.quiet {
|
crate::util::VERBOSITY.store(
|
||||||
true => 0,
|
match cli.quiet {
|
||||||
false => (cli.verbose + 1).into(),
|
true => 0,
|
||||||
}, Ordering::Relaxed);
|
false => (cli.verbose + 1).into(),
|
||||||
|
},
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
GitsyCli {
|
GitsyCli {
|
||||||
path: config_path,
|
path: config_path,
|
||||||
dir: config_dir,
|
dir: config_dir,
|
||||||
@@ -63,7 +65,6 @@ impl GitsyCli {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct GitsySettingsTemplates {
|
pub struct GitsySettingsTemplates {
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
@@ -102,7 +103,7 @@ macro_rules! output_path_fn {
|
|||||||
(true, true) => {
|
(true, true) => {
|
||||||
let name = repo.map(|x| &x.name).unwrap();
|
let name = repo.map(|x| &x.name).unwrap();
|
||||||
tmpl_str.replace("%REPO%", name)
|
tmpl_str.replace("%REPO%", name)
|
||||||
},
|
}
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
panic!("%REPO% variable not available for output path: {}", tmpl_str);
|
panic!("%REPO% variable not available for output path: {}", tmpl_str);
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,7 @@ macro_rules! output_path_fn {
|
|||||||
(true, true) => {
|
(true, true) => {
|
||||||
let name = obj.map(|x| &x.$id).unwrap();
|
let name = obj.map(|x| &x.$id).unwrap();
|
||||||
tmpl_str.replace("%ID%", name)
|
tmpl_str.replace("%ID%", name)
|
||||||
},
|
}
|
||||||
(true, false) => {
|
(true, false) => {
|
||||||
panic!("%ID% variable not available for output path: {}", tmpl_str);
|
panic!("%ID% variable not available for output path: {}", tmpl_str);
|
||||||
}
|
}
|
||||||
@@ -124,12 +125,12 @@ macro_rules! output_path_fn {
|
|||||||
match $is_dir {
|
match $is_dir {
|
||||||
true => {
|
true => {
|
||||||
let _ = create_dir_all(&path);
|
let _ = create_dir_all(&path);
|
||||||
},
|
}
|
||||||
false => {
|
false => {
|
||||||
if let Some(dir) = path.parent() {
|
if let Some(dir) = path.parent() {
|
||||||
let _ = create_dir_all(dir);
|
let _ = create_dir_all(dir);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
path.to_str()
|
path.to_str()
|
||||||
.expect(&format!("Output is not a valid path: {}", path.display()))
|
.expect(&format!("Output is not a valid path: {}", path.display()))
|
||||||
@@ -140,17 +141,17 @@ macro_rules! output_path_fn {
|
|||||||
//step_map_first!(boil_in_wort, Boil, Wort, |b: &Boil| { b.wort_start() });
|
//step_map_first!(boil_in_wort, Boil, Wort, |b: &Boil| { b.wort_start() });
|
||||||
|
|
||||||
impl GitsySettingsOutputs {
|
impl GitsySettingsOutputs {
|
||||||
output_path_fn!(repo_list, GitObject, full_hash, false, "repos.html");
|
output_path_fn!(repo_list, GitObject, full_hash, false, "repos.html");
|
||||||
output_path_fn!(repo_summary, GitObject, full_hash, false, "%REPO%/summary.html");
|
output_path_fn!(repo_summary, GitObject, full_hash, false, "%REPO%/summary.html");
|
||||||
output_path_fn!(commit, GitObject, full_hash, false, "%REPO%/commit/%ID%.html");
|
output_path_fn!(commit, GitObject, full_hash, false, "%REPO%/commit/%ID%.html");
|
||||||
output_path_fn!(branch, GitObject, full_hash, false, "%REPO%/branch/%ID%.html");
|
output_path_fn!(branch, GitObject, full_hash, false, "%REPO%/branch/%ID%.html");
|
||||||
output_path_fn!(tag, GitObject, full_hash, false, "%REPO%/tag/%ID%.html");
|
output_path_fn!(tag, GitObject, full_hash, false, "%REPO%/tag/%ID%.html");
|
||||||
output_path_fn!(file, GitFile, id, false, "%REPO%/file/%ID%.html");
|
output_path_fn!(file, GitFile, id, false, "%REPO%/file/%ID%.html");
|
||||||
output_path_fn!(syntax_css, GitObject, full_hash, false, "%REPO%/file/syntax.css");
|
output_path_fn!(syntax_css, GitObject, full_hash, false, "%REPO%/file/syntax.css");
|
||||||
output_path_fn!(dir, GitFile, id, false, "%REPO%/dir/%ID%.html");
|
output_path_fn!(dir, GitFile, id, false, "%REPO%/dir/%ID%.html");
|
||||||
output_path_fn!(error, GitObject, full_hash, false, "404.html");
|
output_path_fn!(error, GitObject, full_hash, false, "404.html");
|
||||||
output_path_fn!(global_assets, GitObject, full_hash, true, "assets/");
|
output_path_fn!(global_assets, GitObject, full_hash, true, "assets/");
|
||||||
output_path_fn!(repo_assets , GitObject, full_hash, true, "%REPO%/assets/");
|
output_path_fn!(repo_assets, GitObject, full_hash, true, "%REPO%/assets/");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Deserialize, Default, Debug)]
|
#[derive(Clone, Deserialize, Default, Debug)]
|
||||||
@@ -222,22 +223,26 @@ impl GitsySettings {
|
|||||||
let settings: GitsySettings = toml::from_str(&toml).expect("Configuration file is invalid.");
|
let settings: GitsySettings = toml::from_str(&toml).expect("Configuration file is invalid.");
|
||||||
|
|
||||||
// Settings are valid, so let's move into the directory with the config file
|
// Settings are valid, so let's move into the directory with the config file
|
||||||
if cli.dir.to_str().unwrap_or_default().len() > 0 { // empty string means current directory
|
if cli.dir.to_str().unwrap_or_default().len() > 0 {
|
||||||
|
// empty string means current directory
|
||||||
std::env::set_current_dir(&cli.dir)
|
std::env::set_current_dir(&cli.dir)
|
||||||
.expect(&format!("Unable to set working directory to: {}", cli.dir.display()));
|
.expect(&format!("Unable to set working directory to: {}", cli.dir.display()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a list of all remaining TOML "tables" in the file.
|
// Get a list of all remaining TOML "tables" in the file.
|
||||||
// These are the user-supplied individual repositories.
|
// These are the user-supplied individual repositories.
|
||||||
let reserved_keys = vec!("gitsy_templates", "gitsy_outputs", "gitsy_extra");
|
let reserved_keys = vec!["gitsy_templates", "gitsy_outputs", "gitsy_extra"];
|
||||||
let settings_raw: HashMap<String, toml::Value> = toml::from_str(&toml).expect("blah");
|
let settings_raw: HashMap<String, toml::Value> = toml::from_str(&toml).expect("blah");
|
||||||
let table_keys: Vec<String> = settings_raw.iter().filter_map(|x| match x.1.is_table() {
|
let table_keys: Vec<String> = settings_raw
|
||||||
true => match reserved_keys.contains(&x.0.as_str()) {
|
.iter()
|
||||||
false => Some(x.0.clone()),
|
.filter_map(|x| match x.1.is_table() {
|
||||||
true => None,
|
true => match reserved_keys.contains(&x.0.as_str()) {
|
||||||
},
|
false => Some(x.0.clone()),
|
||||||
false => None
|
true => None,
|
||||||
}).collect();
|
},
|
||||||
|
false => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
// Try to convert each unknown "table" into a repo struct, and
|
// Try to convert each unknown "table" into a repo struct, and
|
||||||
// save the ones that are successful. If no repo name is
|
// save the ones that are successful. If no repo name is
|
||||||
@@ -245,14 +250,18 @@ impl GitsySettings {
|
|||||||
let mut repo_descriptions: HashSet<GitsySettingsRepo> = HashSet::new();
|
let mut repo_descriptions: HashSet<GitsySettingsRepo> = HashSet::new();
|
||||||
macro_rules! global_to_repo {
|
macro_rules! global_to_repo {
|
||||||
($settings:ident, $repo:ident, $field:ident) => {
|
($settings:ident, $repo:ident, $field:ident) => {
|
||||||
if $repo.$field.is_none() { $repo.$field = $settings.$field.clone() }
|
if $repo.$field.is_none() {
|
||||||
}
|
$repo.$field = $settings.$field.clone()
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
for k in &table_keys {
|
for k in &table_keys {
|
||||||
let v = settings_raw.get(k).unwrap();
|
let v = settings_raw.get(k).unwrap();
|
||||||
match toml::from_str::<GitsySettingsRepo>(&v.to_string()) {
|
match toml::from_str::<GitsySettingsRepo>(&v.to_string()) {
|
||||||
Ok(mut repo) => {
|
Ok(mut repo) => {
|
||||||
if repo.name.is_none() { repo.name = Some(k.clone()); }
|
if repo.name.is_none() {
|
||||||
|
repo.name = Some(k.clone());
|
||||||
|
}
|
||||||
global_to_repo!(settings, repo, render_markdown);
|
global_to_repo!(settings, repo, render_markdown);
|
||||||
global_to_repo!(settings, repo, syntax_highlight);
|
global_to_repo!(settings, repo, syntax_highlight);
|
||||||
global_to_repo!(settings, repo, syntax_highlight_theme);
|
global_to_repo!(settings, repo, syntax_highlight_theme);
|
||||||
@@ -266,10 +275,10 @@ impl GitsySettings {
|
|||||||
global_to_repo!(settings, repo, limit_total_size);
|
global_to_repo!(settings, repo, limit_total_size);
|
||||||
|
|
||||||
repo_descriptions.insert(repo);
|
repo_descriptions.insert(repo);
|
||||||
},
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to parse repo [{}]: {:?}", k, e);
|
error!("Failed to parse repo [{}]: {:?}", k, e);
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -297,8 +306,8 @@ impl GitsySettings {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
_ => {},
|
_ => {}
|
||||||
}
|
}
|
||||||
(settings, repo_descriptions)
|
(settings, repo_descriptions)
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-20
@@ -1,16 +1,13 @@
|
|||||||
use chrono::{
|
|
||||||
DateTime,
|
|
||||||
offset::FixedOffset,
|
|
||||||
naive::NaiveDateTime,
|
|
||||||
};
|
|
||||||
use crate::git::GitFile;
|
use crate::git::GitFile;
|
||||||
|
use chrono::{naive::NaiveDateTime, offset::FixedOffset, DateTime};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tera::{Filter, Function, Value, to_value, try_get_value};
|
use tera::{to_value, try_get_value, Filter, Function, Value};
|
||||||
|
|
||||||
fn ts_to_date(ts: i64, offset: Option<i64>, format: Option<String>) -> String {
|
fn ts_to_date(ts: i64, offset: Option<i64>, format: Option<String>) -> String {
|
||||||
let offset = offset.unwrap_or(0);
|
let offset = offset.unwrap_or(0);
|
||||||
let dt = NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("Invalid timestamp");
|
let dt = NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("Invalid timestamp");
|
||||||
let dt_tz: DateTime<FixedOffset> = DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
|
let dt_tz: DateTime<FixedOffset> =
|
||||||
|
DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
|
||||||
match format {
|
match format {
|
||||||
Some(f) => dt_tz.format(&f).to_string(),
|
Some(f) => dt_tz.format(&f).to_string(),
|
||||||
None => dt_tz.format("%Y-%m-%d").to_string(),
|
None => dt_tz.format("%Y-%m-%d").to_string(),
|
||||||
@@ -20,32 +17,37 @@ fn ts_to_date(ts: i64, offset: Option<i64>, format: Option<String>) -> String {
|
|||||||
fn ts_to_git_timestamp(ts: i64, offset: Option<i64>) -> String {
|
fn ts_to_git_timestamp(ts: i64, offset: Option<i64>) -> String {
|
||||||
let offset = offset.unwrap_or(0);
|
let offset = offset.unwrap_or(0);
|
||||||
let dt = chrono::naive::NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("invalid timestamp");
|
let dt = chrono::naive::NaiveDateTime::from_timestamp_opt(ts + offset, 0).expect("invalid timestamp");
|
||||||
let dt_tz: DateTime<FixedOffset> = DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
|
let dt_tz: DateTime<FixedOffset> =
|
||||||
|
DateTime::from_local(dt, FixedOffset::east_opt(offset as i32).expect("Invalid timezone"));
|
||||||
dt_tz.format("%a %b %e %T %Y %z").to_string()
|
dt_tz.format("%a %b %e %T %Y %z").to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FileFilter;
|
pub struct FileFilter;
|
||||||
impl Filter for FileFilter {
|
impl Filter for FileFilter {
|
||||||
fn filter(&self, value: &Value, _args: &HashMap<String, Value>
|
fn filter(&self, value: &Value, _args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
|
||||||
) -> Result<Value, tera::Error> {
|
|
||||||
let file_list: Vec<GitFile> = try_get_value!("only_files", "value", Vec<GitFile>, value);
|
let file_list: Vec<GitFile> = try_get_value!("only_files", "value", Vec<GitFile>, value);
|
||||||
let file_list: Vec<GitFile> = file_list.iter().filter_map(|x| match x.kind.as_str() {
|
let file_list: Vec<GitFile> = file_list
|
||||||
"file" => Some(x.clone()),
|
.iter()
|
||||||
_ => None,
|
.filter_map(|x| match x.kind.as_str() {
|
||||||
}).collect();
|
"file" => Some(x.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
Ok(to_value(file_list).unwrap())
|
Ok(to_value(file_list).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DirFilter;
|
pub struct DirFilter;
|
||||||
impl Filter for DirFilter {
|
impl Filter for DirFilter {
|
||||||
fn filter(&self, value: &Value, _args: &HashMap<String, Value>
|
fn filter(&self, value: &Value, _args: &HashMap<String, Value>) -> Result<Value, tera::Error> {
|
||||||
) -> Result<Value, tera::Error> {
|
|
||||||
let file_list: Vec<GitFile> = try_get_value!("only_dirs", "value", Vec<GitFile>, value);
|
let file_list: Vec<GitFile> = try_get_value!("only_dirs", "value", Vec<GitFile>, value);
|
||||||
let file_list: Vec<GitFile> = file_list.iter().filter_map(|x| match x.kind.as_str() {
|
let file_list: Vec<GitFile> = file_list
|
||||||
"dir" => Some(x.clone()),
|
.iter()
|
||||||
_ => None,
|
.filter_map(|x| match x.kind.as_str() {
|
||||||
}).collect();
|
"dir" => Some(x.clone()),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
Ok(to_value(file_list).unwrap())
|
Ok(to_value(file_list).unwrap())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user