🏡 index : ~doyle/rgit.git

author Jordan Doyle <jordan@doyle.la> 2022-07-06 11:46:31.0 +01:00:00
committer Jordan Doyle <jordan@doyle.la> 2022-07-06 11:46:31.0 +01:00:00
commit
204eb55d23d186c138fb7cab18e6f6555352bda8 [patch]
tree
2153d5b6c23fd4caf9faeb95d6cd6e9ea817eb78
parent
576eb8deffcf4f69201d51a325c17f1e8d2d30ee
download
204eb55d23d186c138fb7cab18e6f6555352bda8.tar.gz

Implement pagination on log view



Diff

 src/git.rs               | 53 +++++++++++++++++++++++++++++++++++++++++++++--------
 statics/style.css        |  9 +++++++++
 src/methods/repo.rs      | 22 ++++++++++++++++++++++
 templates/repo/log.html  |  6 ++++++
 templates/repo/refs.html | 27 ++++++++++++++++++++++-----
 5 files changed, 87 insertions(+), 30 deletions(-)

diff --git a/src/git.rs b/src/git.rs
index a29fe9d..0f5ef48 100644
--- a/src/git.rs
+++ a/src/git.rs
@@ -7,25 +7,35 @@
};

use arc_swap::ArcSwapOption;
use git2::{Oid, Repository, Signature, Sort};
use git2::{ObjectType, Oid, Repository, Signature};
use moka::future::Cache;
use time::OffsetDateTime;

pub type RepositoryMetadataList = BTreeMap<Option<String>, Vec<RepositoryMetadata>>;

#[derive(Clone)]
pub struct Git {
    commits: moka::future::Cache<Oid, Arc<Commit>>,
    readme_cache: moka::future::Cache<PathBuf, Arc<str>>,
    refs: moka::future::Cache<PathBuf, Arc<Refs>>,
    commits: Cache<Oid, Arc<Commit>>,
    readme_cache: Cache<PathBuf, Arc<str>>,
    refs: Cache<PathBuf, Arc<Refs>>,
    repository_metadata: Arc<ArcSwapOption<RepositoryMetadataList>>,
}

impl Default for Git {
    fn default() -> Self {
        Self {
            commits: moka::future::Cache::new(100),
            readme_cache: moka::future::Cache::new(100),
            refs: moka::future::Cache::new(100),
            commits: Cache::builder()
                .time_to_live(Duration::from_secs(10))
                .max_capacity(100)
                .build(),
            readme_cache: Cache::builder()
                .time_to_live(Duration::from_secs(10))
                .max_capacity(100)
                .build(),
            refs: Cache::builder()
                .time_to_live(Duration::from_secs(10))
                .max_capacity(100)
                .build(),
            repository_metadata: Arc::new(ArcSwapOption::default()),
        }
    }
@@ -69,11 +79,11 @@
                                commit: commit.into(),
                            });
                        } else if ref_.is_tag() {
                            let commit = ref_.peel_to_commit().unwrap();
                            let tag = ref_.peel_to_tag().unwrap();

                            built_refs.tag.push(Tag {
                                name: ref_.shorthand().unwrap().to_string(),
                                commit: commit.into(),
                                tagger: tag.tagger().map(Into::into),
                            });
                        }
                    }
@@ -143,22 +153,27 @@
        repos
    }

    pub async fn get_commits(&self, repo: PathBuf) -> Vec<Commit> {
    pub async fn get_commits(&self, repo: PathBuf, offset: usize) -> (Vec<Commit>, Option<usize>) {
        const AMOUNT: usize = 200;

        tokio::task::spawn_blocking(move || {
            let repo = Repository::open_bare(repo).unwrap();
            let mut revs = repo.revwalk().unwrap();
            revs.set_sorting(Sort::TIME).unwrap();
            revs.push_head().unwrap();

            let mut commits = Vec::with_capacity(200);
            let mut commits: Vec<Commit> = revs
                .skip(offset)
                .take(AMOUNT + 1)
                .map(|rev| {
                    let rev = rev.unwrap();
                    repo.find_commit(rev).unwrap().into()
                })
                .collect();

            for rev in revs.skip(0).take(200) {
                let rev = rev.unwrap();
                let commit = repo.find_commit(rev).unwrap();
                commits.push(commit.into());
            }
            // TODO: avoid having to take + 1 and popping the last commit off
            let next_offset = commits.pop().is_some().then(|| offset + commits.len());

            commits
            (commits, next_offset)
        })
        .await
        .unwrap()
@@ -185,7 +200,7 @@
#[derive(Debug)]
pub struct Tag {
    pub name: String,
    pub commit: Commit,
    pub tagger: Option<CommitUser>,
}

#[derive(Debug)]
diff --git a/statics/style.css b/statics/style.css
index f407288..c0e96ce 100644
--- a/statics/style.css
+++ a/statics/style.css
@@ -33,6 +33,10 @@
    margin-top: 2rem;
}

.text-center {
    text-align: center;
}

.no-hover:hover {
    text-decoration: none;
}
@@ -50,6 +54,11 @@

table tbody tr.has-parent td:first-of-type {
    padding-left: 1rem;
}

tr.separator {
    background: white !important;
    height: 1rem;
}

table pre { margin: 0; }
diff --git a/src/methods/repo.rs b/src/methods/repo.rs
index 6431dba..1de00d8 100644
--- a/src/methods/repo.rs
+++ a/src/methods/repo.rs
@@ -89,22 +89,40 @@
    Html(View { repo }.render().unwrap())
}

#[derive(Deserialize)]
pub struct LogQuery {
    #[serde(rename = "ofs")]
    offset: Option<usize>,
}

#[allow(clippy::unused_async)]
pub async fn handle_log(
    Extension(repo): Extension<Repository>,
    Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
    Extension(git): Extension<Git>,
    Query(query): Query<LogQuery>,
) -> Html<String> {
    #[derive(Template)]
    #[template(path = "repo/log.html")]
    pub struct View {
        repo: Repository,
        commits: Vec<Commit>,
        next_offset: Option<usize>,
    }

    let commits = git.get_commits(repository_path).await;
    let (commits, next_offset) = git
        .get_commits(repository_path, query.offset.unwrap_or(0))
        .await;

    Html(View { repo, commits }.render().unwrap())
    Html(
        View {
            repo,
            commits,
            next_offset,
        }
        .render()
        .unwrap(),
    )
}

#[allow(clippy::unused_async)]
diff --git a/templates/repo/log.html b/templates/repo/log.html
index 47e73f4..ce9dd7a 100644
--- a/templates/repo/log.html
+++ a/templates/repo/log.html
@@ -25,4 +25,10 @@
    {% endfor %}
    </tbody>
</table>

{% if let Some(next_offset) = next_offset %}
<div class="mt-2 text-center">
    <a href="?ofs={{ next_offset }}">[next]</a>
</div>
{% endif %}
{% endblock %}
diff --git a/templates/repo/refs.html b/templates/repo/refs.html
index dc8afc4..6ab2718 100644
--- a/templates/repo/refs.html
+++ a/templates/repo/refs.html
@@ -26,28 +26,37 @@
    </tr>
    {% endfor %}
    </tbody>
</table>

<table class="repositories mt-2">
    <thead>
    <tbody>
    <tr class="separator">
        <td></td>
        <td></td>
        <td></td>
        <td></td>
    </tr>

    <tr>
        <th>Tag</th>
        <th>Download</th>
        <th>Author</th>
        <th>Age</th>
    </tr>
    </thead>

    <tbody>
    {% for tag in refs.tag %}
    {% for tag in refs.tag.iter().rev() %}
    <tr>
        <td><a href="/{{ repo.display() }}/tag/?h={{ tag.name }}">{{ tag.name }}</a></td>
        <td></td>
        <td>
            {% if let Some(tagger) = tag.tagger %}
                <img src="https://www.gravatar.com/avatar/{{ tagger.email_md5() }}?s=13&d=retro" width="13" height="13">
                {{ tagger.name() }}
            {% endif %}
        </td>
        <td>
            <img src="https://www.gravatar.com/avatar/{{ tag.commit.author().email_md5() }}?s=13&d=retro" width="13" height="13">
            {{ tag.commit.author().name() }}
            {% if let Some(tagger) = tag.tagger %}
                {{ tagger.time() }}
            {% endif %}
        </td>
        <td>{{ tag.commit.author().time() }}</td>
    </tr>
    {% endfor %}
    </tbody>