From 91a9d8421108ad37e36d8bea6f34acafb594743f Mon Sep 17 00:00:00 2001 From: Jordan Doyle Date: Wed, 08 Jan 2025 15:21:05 +0700 Subject: [PATCH] Use deserialised BStrs directly from gix --- src/git.rs | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------ src/methods/filters.rs | 2 +- templates/repo/commit.html | 18 +++++++++--------- templates/repo/tag.html | 12 +++++++----- src/methods/repo/diff.rs | 11 ++++++----- src/methods/repo/tag.rs | 3 ++- 6 files changed, 166 insertions(+), 102 deletions(-) diff --git a/src/git.rs b/src/git.rs index 7751a81..a0cf052 100644 --- a/src/git.rs +++ a/src/git.rs @@ -8,20 +8,21 @@ bstr::{BStr, BString, ByteSlice, ByteVec}, diff::blob::{platform::prepare_diff::Operation, Sink}, object::{tree::EntryKind, Kind}, - objs::tree::EntryRef, + objs::{tree::EntryRef, CommitRef, TagRef}, prelude::TreeEntryRefExt, traverse::tree::visit::Action, url::Scheme, ObjectId, ThreadSafeRepository, Url, }; -use itertools::Itertools; +use itertools::{Either, Itertools}; use moka::future::Cache; -use std::borrow::Cow; use std::{ + borrow::Cow, collections::{BTreeMap, VecDeque}, ffi::OsStr, fmt::{self, Arguments, Write}, io::ErrorKind, + iter::Copied, path::{Path, PathBuf}, str::FromStr, sync::Arc, @@ -30,8 +31,10 @@ use tar::Builder; use time::{OffsetDateTime, UtcOffset}; use tracing::{error, instrument, warn}; +use yoke::{Yoke, Yokeable}; use crate::{ + methods::filters::DisplayHexBuffer, syntax_highlight::{format_file, format_file_inner, ComrakHighlightAdapter, FileIdentifier}, unified_diff_builder::{Callback, UnifiedDiffBuilder}, }; @@ -272,7 +275,7 @@ } #[instrument(skip(self))] - pub async fn tag_info(self: Arc) -> Result { + pub async fn tag_info(self: Arc) -> Result, Vec>> { tokio::task::spawn_blocking(move || { let tag_name = self.branch.clone().context("no tag given")?; let repo = self.repo.to_thread_local(); @@ -281,25 +284,25 @@ .find_reference(&format!("refs/tags/{tag_name}")) .context("Given tag does not exist in repository")? .peel_to_tag() - .context("Couldn't get to a tag from the given reference")?; - let tag_target = tag - .target_id() - .context("Couldn't find tagged object")? - .object()?; - - let tagged_object = match tag_target.kind { - Kind::Commit => Some(TaggedObject::Commit(tag_target.id.to_string())), - Kind::Tree => Some(TaggedObject::Tree(tag_target.id.to_string())), - _ => None, - }; - - let tag_info = tag.decode()?; - - Ok(DetailedTag { - name: tag_name, - tagger: tag_info.tagger.map(TryInto::try_into).transpose()?, - message: tag_info.message.to_string(), - tagged_object, + .context("Couldn't get to a tag from the given reference")? + .detach() + .data; + + Yoke::try_attach_to_cart(tag, move |tag| { + let tag = TagRef::from_bytes(tag)?; + + let tagged_object = match tag.target_kind { + Kind::Commit => Some(TaggedObject::Commit(tag.target)), + Kind::Tree => Some(TaggedObject::Tree(tag.target)), + _ => None, + }; + + Ok::<_, anyhow::Error>(DetailedTag { + name: tag_name, + tagger: tag.tagger.map(TryInto::try_into).transpose()?, + tagged_object, + message: tag.message, + }) }) }) .await @@ -393,10 +396,16 @@ .context("Couldn't find commit HEAD of repository refers to")?; let (diff_output, diff_stats) = fetch_diff_and_stats(&repo, &commit, highlighted)?; - let mut commit = Commit::try_from(commit)?; - commit.diff_stats = diff_stats; - commit.diff = diff_output; - Ok(commit) + let oid = take_oid(commit.id); + let inner = Yoke::try_attach_to_cart(commit.detach().data, |commit| { + CommitInner::new(CommitRef::from_bytes(commit)?, oid) + })?; + + Ok(Commit { + inner, + diff_stats, + diff: diff_output, + }) }) .await .context("Failed to join Tokio task")? @@ -479,16 +488,28 @@ let (diff_output, diff_stats) = fetch_diff_and_stats(&repo, &commit, highlighted)?; - let mut commit = Commit::try_from(commit)?; - commit.diff_stats = diff_stats; - commit.diff = diff_output; + let oid = take_oid(commit.id); - Ok(Arc::new(commit)) + let inner = Yoke::try_attach_to_cart(commit.detach().data, |commit| { + CommitInner::new(CommitRef::from_bytes(commit)?, oid) + })?; + + Ok(Arc::new(Commit { + inner, + diff_stats, + diff: diff_output, + })) }) .await .context("Failed to join Tokio task")? }) .await + } +} + +fn take_oid(v: ObjectId) -> [u8; 20] { + match v { + ObjectId::Sha1(v) => v, } } @@ -682,33 +703,33 @@ } #[derive(Debug)] -pub enum TaggedObject { - Commit(String), - Tree(String), +pub enum TaggedObject<'a> { + Commit(&'a BStr), + Tree(&'a BStr), } -#[derive(Debug)] -pub struct DetailedTag { +#[derive(Debug, Yokeable)] +pub struct DetailedTag<'a> { pub name: Arc, - pub tagger: Option, - pub message: String, - pub tagged_object: Option, + pub tagger: Option>, + pub message: &'a BStr, + pub tagged_object: Option>, } #[derive(Debug)] -pub struct CommitUser { - name: String, - email: String, +pub struct CommitUser<'a> { + name: &'a BStr, + email: &'a BStr, time: (i64, i32), } -impl TryFrom> for CommitUser { +impl<'a> TryFrom> for CommitUser<'a> { type Error = anyhow::Error; - fn try_from(v: SignatureRef<'_>) -> Result { + fn try_from(v: SignatureRef<'a>) -> Result { Ok(CommitUser { - name: v.name.to_string(), - email: v.email.to_string(), + name: v.name, + email: v.email, time: (v.time.seconds, v.time.offset), // time: OffsetDateTime::from_unix_timestamp(v.time.seconds)? // .to_offset(UtcOffset::from_whole_seconds(v.time.offset)?), @@ -716,12 +737,12 @@ } } -impl CommitUser { - pub fn name(&self) -> &str { +impl CommitUser<'_> { + pub fn name(&self) -> &BStr { &self.name } - pub fn email(&self) -> &str { + pub fn email(&self) -> &BStr { &self.email } @@ -734,63 +755,102 @@ #[derive(Debug)] pub struct Commit { - author: CommitUser, - committer: CommitUser, - oid: String, - tree: String, - parents: Vec, - summary: String, - body: String, + inner: yoke::Yoke, Vec>, pub diff_stats: String, pub diff: String, } -impl TryFrom> for Commit { - type Error = anyhow::Error; +impl Commit { + pub fn get(&self) -> &CommitInner<'_> { + self.inner.get() + } +} - fn try_from(commit: gix::Commit<'_>) -> Result { - let message = commit.message()?; - - Ok(Commit { - author: CommitUser::try_from(commit.author()?)?, - committer: CommitUser::try_from(commit.committer()?)?, - oid: commit.id().to_string(), - tree: commit.tree_id()?.to_string(), - parents: commit.parent_ids().map(|v| v.to_string()).collect(), - summary: message.summary().to_string(), - body: message.body.map_or_else(String::new, ToString::to_string), - diff_stats: String::with_capacity(0), - diff: String::with_capacity(0), +#[derive(Debug, Yokeable)] +pub struct CommitInner<'a> { + author: CommitUser<'a>, + committer: CommitUser<'a>, + oid: [u8; 20], + tree: &'a BStr, + parents: SmallVec<&'a BStr>, + summary: Cow<'a, BStr>, + body: &'a BStr, +} + +#[derive(Debug)] +enum SmallVec { + None, + One(T), + Many(Vec), +} + +impl SmallVec { + fn iter( + &self, + ) -> Either, Either, Copied>>> { + match self { + Self::None => Either::Left(std::iter::empty()), + Self::One(v) => Either::Right(Either::Left(std::iter::once(*v))), + Self::Many(v) => Either::Right(Either::Right(v.iter().copied())), + } + } +} + +impl<'a> CommitInner<'a> { + pub fn new(commit: gix::worktree::object::CommitRef<'a>, oid: [u8; 20]) -> Result { + let message = commit.message(); + + Ok(CommitInner { + author: CommitUser::try_from(commit.author)?, + committer: CommitUser::try_from(commit.committer)?, + oid, + tree: commit.tree, + parents: commit + .parents + .into_inner() + .map(|[v]| SmallVec::One(v)) + .unwrap_or_else(|inner| { + if inner.is_empty() { + SmallVec::None + } else { + SmallVec::Many(inner.into_vec()) + } + }), + + summary: message.summary(), + body: message.body.unwrap_or_else(|| BStr::new("")), }) } } -impl Commit { - pub fn author(&self) -> &CommitUser { +impl CommitInner<'_> { + pub fn author(&self) -> &CommitUser<'_> { &self.author } - pub fn committer(&self) -> &CommitUser { + pub fn committer(&self) -> &CommitUser<'_> { &self.committer } - pub fn oid(&self) -> &str { - &self.oid + pub fn oid(&self) -> DisplayHexBuffer<20> { + let mut buf = const_hex::Buffer::new(); + buf.format(&self.oid); + DisplayHexBuffer(buf) } - pub fn tree(&self) -> &str { + pub fn tree(&self) -> &BStr { &self.tree } - pub fn parents(&self) -> impl Iterator { - self.parents.iter().map(String::as_str) + pub fn parents(&self) -> impl Iterator { + self.parents.iter() } - pub fn summary(&self) -> &str { + pub fn summary(&self) -> &BStr { &self.summary } - pub fn body(&self) -> &str { + pub fn body(&self) -> &BStr { &self.body } } diff --git a/src/methods/filters.rs b/src/methods/filters.rs index 2724abe..a0e15f9 100644 --- a/src/methods/filters.rs +++ a/src/methods/filters.rs @@ -41,7 +41,7 @@ Ok(unix_mode::to_string(u32::from(*s))) } -pub struct DisplayHexBuffer(const_hex::Buffer); +pub struct DisplayHexBuffer(pub const_hex::Buffer); impl Display for DisplayHexBuffer { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/templates/repo/commit.html b/templates/repo/commit.html index 017a89b..820dc61 100644 --- a/templates/repo/commit.html +++ a/templates/repo/commit.html @@ -14,23 +14,23 @@ author - {{ commit.author().name() }} <{{ commit.author().email() }}> - {{ commit.author().time() }} + {{ commit.get().author().name() }} <{{ commit.get().author().email() }}> + {{ commit.get().author().time() }} committer - {{ commit.committer().name() }} <{{ commit.committer().email() }}> - {{ commit.committer().time() }} + {{ commit.get().committer().name() }} <{{ commit.get().committer().email() }}> + {{ commit.get().committer().time() }} commit -
{{ commit.oid() }} [patch]
+
{{ commit.get().oid() }} [patch]
tree -
{{ commit.tree() }}
+
{{ commit.get().tree() }}
- {%- for parent in commit.parents() %} + {%- for parent in commit.get().parents() %} parent
{{ parent }}
@@ -44,8 +44,8 @@ -

{{ commit.summary() }}

-
{{ commit.body() }}
+

{{ commit.get().summary() }}

+
{{ commit.get().body() }}

Diff

{{ commit.diff_stats|safe }}
diff --git a/templates/repo/tag.html b/templates/repo/tag.html
index 94ef63c..b034f2a 100644
--- a/templates/repo/tag.html
+++ a/templates/repo/tag.html
@@ -7,9 +7,9 @@
     
     
         tag name
-        {{ tag.name }}
+        {{ tag.get().name }}
     
-    {% if let Some(tagger) = tag.tagger %}
+    {% if let Some(tagger) = tag.get().tagger %}
         
             tag date
             {{ tagger.time() }}
@@ -19,7 +19,7 @@
             {{ tagger.name() }} <{{ tagger.email() }}>
         
     {% endif %}
-    {% if let Some(tagged_object) = tag.tagged_object %}
+    {% if let Some(tagged_object) = tag.get().tagged_object %}
         
             tagged object
             
@@ -34,11 +34,13 @@
     {% endif %}
     
         download
-        
{{ tag.name }}.tar.gz
+ +
{{ tag.get().name }}.tar.gz
+ -
{{ tag.message }}
+
{{ tag.get().message }}
{% endblock %} diff --git a/src/methods/repo/diff.rs b/src/methods/repo/diff.rs index ed1ee87..d170173 100644 --- a/src/methods/repo/diff.rs +++ a/src/methods/repo/diff.rs @@ -68,18 +68,19 @@ let mut data = BytesMut::new(); - writeln!(data, "From {} Mon Sep 17 00:00:00 2001", commit.oid()).unwrap(); + writeln!(data, "From {} Mon Sep 17 00:00:00 2001", commit.get().oid()).unwrap(); writeln!( data, "From: {} <{}>", - commit.author().name(), - commit.author().email() + commit.get().author().name(), + commit.get().author().email() ) .unwrap(); write!(data, "Date: ").unwrap(); let mut writer = data.writer(); commit + .get() .author() .time() .format_into(&mut writer, &Rfc2822) @@ -87,9 +88,9 @@ let mut data = writer.into_inner(); writeln!(data).unwrap(); - writeln!(data, "Subject: [PATCH] {}\n", commit.summary()).unwrap(); + writeln!(data, "Subject: [PATCH] {}\n", commit.get().summary()).unwrap(); - write!(data, "{}", commit.body()).unwrap(); + write!(data, "{}", commit.get().body()).unwrap(); writeln!(data, "---").unwrap(); diff --git a/src/methods/repo/tag.rs b/src/methods/repo/tag.rs index 0b7dab4..1baea7e 100644 --- a/src/methods/repo/tag.rs +++ a/src/methods/repo/tag.rs @@ -1,8 +1,9 @@ use std::sync::Arc; use askama::Template; use axum::{extract::Query, response::IntoResponse, Extension}; use serde::Deserialize; +use yoke::Yoke; use crate::{ git::DetailedTag, @@ -24,7 +25,7 @@ #[template(path = "repo/tag.html")] pub struct View { repo: Repository, - tag: DetailedTag, + tag: Yoke, Vec>, branch: Option>, } -- rgit 0.1.4