- Commit
- 27f29367f7b1fba8c9432dfd4e6e3cd29ccfa944
- Parent
- c5f5c309f90aa82669789bc9d4a51d4034a6ef53
- Author
- Pablo <pablo-pie@riseup.net>
- Date
Refactored the construction of last_modified_time
Yet another static site generator for Git 🙀️
Refactored the construction of last_modified_time
1 files changed, 77 insertions, 55 deletions
Status | Name | Changes | Insertions | Deletions |
Modified | src/main.rs | 2 files changed | 77 | 55 |
diff --git a/src/main.rs b/src/main.rs @@ -6,9 +6,10 @@ use std::{ env, fmt::{self, Display}, collections::HashMap, - time::{Duration, SystemTime, Instant}, + time::{SystemTime, Instant}, process::ExitCode, os::unix::fs::PermissionsExt, + cell::RefCell, cmp, }; use git2::{ @@ -234,9 +235,14 @@ struct RepoRenderer<'repo> { pub readme: Option<Readme>, pub license: Option<String>, + // stores the seconds since the Unix epoch of the last commit each blob was + // modified at + // + // None if running with --full-build enabled + pub last_commit_time: Option<RefCell<HashMap<Oid, u64>>>, + // cached constants which depend on command-line flags: // these shouldn't be modified at runtime - pub full_build: bool, pub output_path: PathBuf, pub output_root: &'static str, } @@ -330,6 +336,12 @@ impl<'repo> RepoRenderer<'repo> { (PathBuf::from(config::OUTPUT_PATH), "") }; + let last_commit_time = if flags.full_build() { + None + } else { + Some(RefCell::default()) + }; + Ok(Self { name: &repo.name, description: repo.description.as_deref(), @@ -341,7 +353,7 @@ impl<'repo> RepoRenderer<'repo> { readme, license, - full_build: flags.full_build(), + last_commit_time, output_path, output_root, }) @@ -349,11 +361,11 @@ impl<'repo> RepoRenderer<'repo> { pub fn render(&self) -> io::Result<()> { self.render_summary()?; - let last_commit_time = self.render_log()?; + self.render_log()?; if let Some(ref license) = self.license { self.render_license(license)?; } - self.render_tree(&last_commit_time)?; + self.render_tree()?; Ok(()) } @@ -396,7 +408,6 @@ impl<'repo> RepoRenderer<'repo> { pub fn render_tree( &self, - last_commit_time: &HashMap<Oid, SystemTime>, ) -> io::Result<()> { let mut tree_stack = Vec::new(); let mut blob_stack = Vec::new(); @@ -416,7 +427,7 @@ impl<'repo> RepoRenderer<'repo> { } for (blob, path) in blob_stack { - self.render_blob(blob, path, last_commit_time)?; + self.render_blob(blob, path)?; } Ok(()) @@ -571,7 +582,6 @@ impl<'repo> RepoRenderer<'repo> { &self, blob: Blob, path: PathBuf, - last_commit_time: &HashMap<Oid, SystemTime>, ) -> io::Result<()> { let mut page_path = self.output_path.clone(); page_path.push(self.name); @@ -584,9 +594,16 @@ impl<'repo> RepoRenderer<'repo> { // // skip rendering the page if the commit the blob was last updated on is // older than the page - if !self.full_build { + if let Some(ref last_commit_time) = &self.last_commit_time { + let last_commit_time = last_commit_time.borrow(); if let Ok(meta) = fs::metadata(&page_path) { - let last_modified = meta.modified().unwrap(); + let last_modified = meta + .modified() + .unwrap() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + if last_modified > last_commit_time[&blob.id] { return Ok(()); } @@ -696,9 +713,7 @@ impl<'repo> RepoRenderer<'repo> { Ok(()) } - fn render_log(&self) -> io::Result<HashMap<Oid, SystemTime>> { - let mut last_mofied = HashMap::new(); - + fn render_log(&self) -> io::Result<()> { let mut revwalk = self.repo.revwalk().unwrap(); revwalk.push_head().unwrap(); let mut commits = Vec::new(); @@ -774,25 +789,54 @@ impl<'repo> RepoRenderer<'repo> { writeln!(&mut f, "</html>")?; for commit in commits { - self.render_commit(&commit, &mut last_mofied)?; + self.render_commit_and_collect_last_commit_times(&commit)?; } - Ok(last_mofied) + Ok(()) } /// Renders the commit to HTML and updates the access time /// /// Shorcircutes if the commit page already exists. - fn render_commit( + fn render_commit_and_collect_last_commit_times( &self, commit: &Commit<'repo>, - last_commit_time: &mut HashMap<Oid, SystemTime>, ) -> io::Result<()> { - let mut path = self.output_path.clone(); - path.push(self.name); - path.push(COMMIT_SUBDIR); - path.push(format!("{}.html", commit.id())); - let should_skip = !self.full_build && path.exists(); + // ======================================================================== + let diff = self + .repo + .diff_tree_to_tree( + commit.parent(0).and_then(|p| p.tree()).ok().as_ref(), + commit.tree().ok().as_ref(), + None + ).expect("diff between trees should be there"); + + // collect the last time files were modified at + if let Some(ref last_commit_time) = &self.last_commit_time { + for diff_delta in diff.deltas() { + // filter desired deltas + if !matches!(diff_delta.status(), + Delta::Added | Delta::Copied | Delta::Deleted | + Delta::Modified | Delta::Renamed) { + continue; + } + + let new_file = diff_delta.new_file(); + + let mut last_commit_time = last_commit_time.borrow_mut(); + let id = new_file.id(); + let commit_time = commit.time().seconds() as u64; + if let Some(time) = last_commit_time.get_mut(&id) { + // the newest time is NOT garanteed by + // the order we loop through the commits + if *time < commit_time { + *time = commit_time; + } + } else { + last_commit_time.insert(id, commit_time); + } + } + } // ======================================================================== #[derive(Debug)] @@ -810,23 +854,25 @@ impl<'repo> RepoRenderer<'repo> { is_binary: bool, } + let mut path = self.output_path.clone(); + path.push(self.name); + path.push(COMMIT_SUBDIR); + path.push(format!("{}.html", commit.id())); + + // skip rendering the commit page if the file already exists + if self.last_commit_time.is_some() && path.exists() { + return Ok(()); + } + let sig = commit.author(); let time = sig.when(); - let diff = self - .repo - .diff_tree_to_tree( - commit.parent(0).and_then(|p| p.tree()).ok().as_ref(), - commit.tree().ok().as_ref(), - None - ).expect("diff between trees should be there"); - let deltas_iter = diff.deltas(); let mut deltas: Vec<DeltaInfo<'_>> = Vec::with_capacity(deltas_iter.len()); for (delta_id, diff_delta) in deltas_iter.enumerate() { // filter desired deltas if !matches!(diff_delta.status(), - Delta::Added | Delta::Copied | Delta::Deleted | + Delta::Added | Delta::Copied | Delta::Deleted | Delta::Modified | Delta::Renamed) { continue; } @@ -836,25 +882,6 @@ impl<'repo> RepoRenderer<'repo> { let old_path = &old_file.path().unwrap(); let new_path = &new_file.path().unwrap(); - // collect the last time a file was modified at - let id = new_file.id(); - let commit_time = Duration::from_secs(commit.time().seconds() as u64); - let commit_time = SystemTime::UNIX_EPOCH + commit_time; - if let Some(time) = last_commit_time.get_mut(&id) { - // the newest time is NOT garanteed by - // the order we loop through the commits - if *time < commit_time { - *time = commit_time; - } - } else { - last_commit_time.insert(id, commit_time); - } - - // TODO: [optmize]: refactor this? avoid late-stage decision making - if should_skip { - continue; - } - let patch = Patch::from_diff(&diff, delta_id) .unwrap() .expect("diff should have patch"); @@ -893,11 +920,6 @@ impl<'repo> RepoRenderer<'repo> { } // ======================================================================== - // skip rendering the commit page if the file already exists - if should_skip { - return Ok(()); - } - // NOTE: this is an expensive operation, taking upwards of 76% of // execution-time: Diff::stats should only be called when we // know for the page needs updating