🏡 index : ~doyle/rgit.git

author Jordan Doyle <jordan@doyle.la> 2024-01-21 11:43:11.0 +00:00:00
committer Jordan Doyle <jordan@doyle.la> 2024-01-21 11:43:11.0 +00:00:00
commit
a254a4a08146cca551382da78032e58dfc302b3d [patch]
tree
d0eb9e368bc1812a72a52186a8c00c138ced81a2
parent
d5e06c56fc5a3b9323382927dce9db3df20a0191
download
a254a4a08146cca551382da78032e58dfc302b3d.tar.gz

Expose page generation time



Diff

 src/main.rs                     | 46 ++++++++++++++++++++++++++++++++++++++++++----
 templates/base.html             |  4 +++-
 templates/index.html            |  3 ++-
 src/layers/logger.rs            |  9 +++++++--
 src/methods/filters.rs          |  7 +++++++
 src/methods/index.rs            | 21 +++++++++++++--------
 src/methods/repo/about.rs       | 11 +++++++----
 src/methods/repo/commit.rs      | 11 +++++++----
 src/methods/repo/diff.rs        |  9 ++++++---
 src/methods/repo/log.rs         | 13 +++++--------
 src/methods/repo/refs.rs        |  6 +++---
 src/methods/repo/summary.rs     | 14 ++++++--------
 src/methods/repo/tag.rs         | 11 +++++++----
 src/methods/repo/tree.rs        | 38 ++++++++++++++++++++++----------------
 templates/repo/macros/refs.html |  1 +
 15 files changed, 127 insertions(+), 77 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index ee61a4c..fb55d94 100644
--- a/src/main.rs
+++ a/src/main.rs
@@ -315,17 +315,41 @@
    Box::from(out)
}

#[instrument(skip(t))]
pub fn into_response<T: Template>(t: &T) -> Response {
    match t.render() {
        Ok(body) => {
            let headers = [(
                http::header::CONTENT_TYPE,
                HeaderValue::from_static(T::MIME_TYPE),
            )];

            (headers, body).into_response()
pub struct TemplateResponse<T> {
    template: T,
}

impl<T: Template> IntoResponse for TemplateResponse<T> {
    #[instrument(skip_all)]
    fn into_response(self) -> Response {
        match self.template.render() {
            Ok(body) => {
                let headers = [(
                    http::header::CONTENT_TYPE,
                    HeaderValue::from_static(T::MIME_TYPE),
                )];

                (headers, body).into_response()
            }
            Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
        }
    }
}

pub fn into_response<T: Template>(template: T) -> impl IntoResponse {
    TemplateResponse { template }
}

pub enum ResponseEither<A, B> {
    Left(A),
    Right(B),
}

impl<A: IntoResponse, B: IntoResponse> IntoResponse for ResponseEither<A, B> {
    fn into_response(self) -> Response {
        match self {
            Self::Left(a) => a.into_response(),
            Self::Right(b) => b.into_response(),
        }
        Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
    }
}
diff --git a/templates/base.html b/templates/base.html
index 3000365..34eef63 100644
--- a/templates/base.html
+++ a/templates/base.html
@@ -35,7 +35,9 @@
</main>

<footer>
    generated by <a href="https://git.inept.dev/~doyle/rgit.git/about" target="_blank">rgit</a> v{{ crate::CRATE_VERSION }} at {{ time::OffsetDateTime::now_utc().to_string() }}
    generated by <a href="https://git.inept.dev/~doyle/rgit.git/about" target="_blank">rgit</a> v{{ crate::CRATE_VERSION }}
    at {{ time::OffsetDateTime::now_utc()|format_time }}
    in {{ "{:?}"|format(crate::layers::logger::REQ_TIMESTAMP.get().elapsed()) }}
</footer>
</body>
</html>
diff --git a/templates/index.html b/templates/index.html
index 2544395..a15551e 100644
--- a/templates/index.html
+++ a/templates/index.html
@@ -12,12 +12,13 @@
        </thead>

        <tbody>
        {%- for (path, repositories) in repositories.iter() %}
        {%- for (path, repositories) in repositories %}
            {%- if let Some(path) = path %}
            <tr><td class="repo-section" colspan="4">{{ path }}</td></tr>
            {%- endif -%}

            {%- for repository in repositories %}
            {% set repository = repository.get() %}
            <tr class="{% if path.is_some() %}has-parent{% endif %}">
                <td>
                    <a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
diff --git a/src/layers/logger.rs b/src/layers/logger.rs
index 5ccdc3e..0affeea 100644
--- a/src/layers/logger.rs
+++ a/src/layers/logger.rs
@@ -12,6 +12,7 @@
    http::{HeaderValue, Method, Request, Response},
};
use futures::future::{Future, FutureExt, Join, Map, Ready};
use tokio::task::futures::TaskLocalFuture;
use tower_service::Service;
use tracing::{error, info, instrument::Instrumented, Instrument, Span};
use uuid::Uuid;
@@ -37,7 +38,7 @@
    type Response = S::Response;
    type Error = S::Error;
    type Future = Map<
        Join<Instrumented<S::Future>, Ready<PendingLogMessage>>,
        Join<TaskLocalFuture<Instant, Instrumented<S::Future>>, Ready<PendingLogMessage>>,
        fn((<S::Future as Future>::Output, PendingLogMessage)) -> <S::Future as Future>::Output,
    >;

@@ -63,7 +64,7 @@
        };

        futures::future::join(
            self.0.call(req).instrument(span),
            REQ_TIMESTAMP.scope(log_message.start, self.0.call(req).instrument(span)),
            futures::future::ready(log_message),
        )
        .map(|(response, pending_log_message)| {
@@ -76,6 +77,10 @@
            Ok(response)
        })
    }
}

tokio::task_local! {
    pub static REQ_TIMESTAMP: Instant;
}

pub struct PendingLogMessage {
diff --git a/src/methods/filters.rs b/src/methods/filters.rs
index f6d6f6b..273bb7e 100644
--- a/src/methods/filters.rs
+++ a/src/methods/filters.rs
@@ -1,7 +1,14 @@
// sorry clippy, we don't have a choice. askama forces this on us
#![allow(clippy::unnecessary_wraps, clippy::trivially_copy_pass_by_ref)]

use std::borrow::Borrow;
use time::format_description::well_known::Rfc3339;

pub fn format_time(s: time::OffsetDateTime) -> Result<String, askama::Error> {
    s.format(&Rfc3339)
        .map_err(Box::from)
        .map_err(askama::Error::Custom)
}

pub fn timeago(s: impl Borrow<time::OffsetDateTime>) -> Result<String, askama::Error> {
    Ok(timeago::Formatter::new()
diff --git a/src/methods/index.rs b/src/methods/index.rs
index 40a8b8c..ed10048 100644
--- a/src/methods/index.rs
+++ a/src/methods/index.rs
@@ -1,35 +1,38 @@
use std::{collections::BTreeMap, sync::Arc};

use anyhow::Context;
use askama::Template;
use axum::{response::Response, Extension};
use axum::{response::IntoResponse, Extension};

use super::filters;
use crate::{database::schema::repository::Repository, into_response};
use crate::{
    database::schema::repository::{Repository, YokedRepository},
    into_response,
};

#[derive(Template)]
#[template(path = "index.html")]
pub struct View<'a> {
    pub repositories: BTreeMap<Option<String>, Vec<&'a Repository<'a>>>,
pub struct View {
    pub repositories: BTreeMap<Option<String>, Vec<YokedRepository>>,
}

pub async fn handle(
    Extension(db): Extension<Arc<rocksdb::DB>>,
) -> Result<Response, super::repo::Error> {
    let mut repositories: BTreeMap<Option<String>, Vec<&Repository<'_>>> = BTreeMap::new();
) -> Result<impl IntoResponse, super::repo::Error> {
    let mut repositories: BTreeMap<Option<String>, Vec<YokedRepository>> = BTreeMap::new();

    let fetched = tokio::task::spawn_blocking(move || Repository::fetch_all(&db))
        .await
        .context("Failed to join Tokio task")??;
    for (k, v) in &fetched {
    for (k, v) in fetched {
        // TODO: fixme
        let mut split: Vec<_> = k.split('/').collect();
        split.pop();
        let key = Some(split.join("/")).filter(|v| !v.is_empty());

        let k = repositories.entry(key).or_default();
        k.push(v.get());
        k.push(v);
    }

    Ok(into_response(&View { repositories }))
    Ok(into_response(View { repositories }))
}
diff --git a/src/methods/repo/about.rs b/src/methods/repo/about.rs
index f8e86cf..b597f31 100644
--- a/src/methods/repo/about.rs
+++ a/src/methods/repo/about.rs
@@ -1,13 +1,16 @@
use std::sync::Arc;

use askama::Template;
use axum::{extract::Query, response::Response, Extension};
use axum::{extract::Query, response::IntoResponse, Extension};
use serde::Deserialize;

use crate::{
    git::ReadmeFormat,
    into_response,
    methods::repo::{Repository, RepositoryPath, Result},
    methods::{
        filters,
        repo::{Repository, RepositoryPath, Result},
    },
    Git,
};

@@ -30,14 +33,14 @@
    Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
    Extension(git): Extension<Arc<Git>>,
    Query(query): Query<UriQuery>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    let open_repo = git
        .clone()
        .repo(repository_path, query.branch.clone())
        .await?;
    let readme = open_repo.readme().await?;

    Ok(into_response(&View {
    Ok(into_response(View {
        repo,
        readme,
        branch: query.branch,
diff --git a/src/methods/repo/commit.rs b/src/methods/repo/commit.rs
index b339ff8..e089ab7 100644
--- a/src/methods/repo/commit.rs
+++ a/src/methods/repo/commit.rs
@@ -1,13 +1,16 @@
use std::sync::Arc;

use askama::Template;
use axum::{extract::Query, response::Response, Extension};
use axum::{extract::Query, response::IntoResponse, Extension};
use serde::Deserialize;

use crate::{
    git::Commit,
    into_response,
    methods::repo::{Repository, RepositoryPath, Result},
    methods::{
        filters,
        repo::{Repository, RepositoryPath, Result},
    },
    Git,
};

@@ -33,7 +36,7 @@
    Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
    Extension(git): Extension<Arc<Git>>,
    Query(query): Query<UriQuery>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    let open_repo = git.repo(repository_path, query.branch.clone()).await?;

    let dl_branch = if let Some(branch) = query.branch.clone() {
@@ -56,7 +59,7 @@
        Arc::new(open_repo.latest_commit().await?)
    };

    Ok(into_response(&View {
    Ok(into_response(View {
        repo,
        commit,
        branch: query.branch,
diff --git a/src/methods/repo/diff.rs b/src/methods/repo/diff.rs
index ebc7e25..0a6a6c6 100644
--- a/src/methods/repo/diff.rs
+++ a/src/methods/repo/diff.rs
@@ -11,7 +11,10 @@
use crate::{
    git::Commit,
    http, into_response,
    methods::repo::{commit::UriQuery, Repository, RepositoryPath, Result},
    methods::{
        filters,
        repo::{commit::UriQuery, Repository, RepositoryPath, Result},
    },
    Git,
};

@@ -28,7 +31,7 @@
    Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
    Extension(git): Extension<Arc<Git>>,
    Query(query): Query<UriQuery>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    let open_repo = git.repo(repository_path, query.branch.clone()).await?;
    let commit = if let Some(commit) = query.id {
        open_repo.commit(&commit).await?
@@ -36,7 +39,7 @@
        Arc::new(open_repo.latest_commit().await?)
    };

    Ok(into_response(&View {
    Ok(into_response(View {
        repo,
        commit,
        branch: query.branch,
diff --git a/src/methods/repo/log.rs b/src/methods/repo/log.rs
index 80f4b1e..1882317 100644
--- a/src/methods/repo/log.rs
+++ a/src/methods/repo/log.rs
@@ -1,10 +1,9 @@
use std::sync::Arc;

use anyhow::Context;
use askama::Template;
use axum::{extract::Query, response::Response, Extension};
use axum::{extract::Query, response::IntoResponse, Extension};
use serde::Deserialize;
use yoke::Yoke;

use crate::{
    database::schema::{commit::YokedCommit, repository::YokedRepository},
@@ -25,9 +24,9 @@

#[derive(Template)]
#[template(path = "repo/log.html")]
pub struct View<'a> {
pub struct View {
    repo: Repository,
    commits: Vec<&'a crate::database::schema::commit::Commit<'a>>,
    commits: Vec<YokedCommit>,
    next_offset: Option<u64>,
    branch: Option<String>,
}
@@ -36,7 +35,7 @@
    Extension(repo): Extension<Repository>,
    Extension(db): Extension<Arc<rocksdb::DB>>,
    Query(query): Query<UriQuery>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    tokio::task::spawn_blocking(move || {
        let offset = query.offset.unwrap_or(0);

@@ -52,9 +51,7 @@
            None
        };

        let commits = commits.iter().map(Yoke::get).collect();

        Ok(into_response(&View {
        Ok(into_response(View {
            repo,
            commits,
            next_offset,
diff --git a/src/methods/repo/refs.rs b/src/methods/repo/refs.rs
index 660065b..9ed2814 100644
--- a/src/methods/repo/refs.rs
+++ a/src/methods/repo/refs.rs
@@ -1,8 +1,8 @@
use std::{collections::BTreeMap, sync::Arc};

use anyhow::Context;
use askama::Template;
use axum::{response::Response, Extension};
use axum::{response::IntoResponse, Extension};

use crate::{
    into_response,
@@ -23,7 +23,7 @@
pub async fn handle(
    Extension(repo): Extension<Repository>,
    Extension(db): Extension<Arc<rocksdb::DB>>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    tokio::task::spawn_blocking(move || {
        let repository = crate::database::schema::repository::Repository::open(&db, &*repo)?
            .context("Repository does not exist")?;
@@ -40,7 +40,7 @@

        let tags = repository.get().tag_tree(db).fetch_all()?;

        Ok(into_response(&View {
        Ok(into_response(View {
            repo,
            refs: Refs { heads, tags },
            branch: None,
diff --git a/src/methods/repo/summary.rs b/src/methods/repo/summary.rs
index 3d6d5b4..d5cd176 100644
--- a/src/methods/repo/summary.rs
+++ a/src/methods/repo/summary.rs
@@ -1,9 +1,8 @@
use std::{collections::BTreeMap, sync::Arc};

use anyhow::Context;
use askama::Template;
use axum::{response::Response, Extension};
use yoke::Yoke;
use axum::{response::IntoResponse, Extension};

use crate::{
    database::schema::{commit::YokedCommit, repository::YokedRepository},
@@ -16,22 +15,21 @@

#[derive(Template)]
#[template(path = "repo/summary.html")]
pub struct View<'a> {
pub struct View {
    repo: Repository,
    refs: Refs,
    commit_list: Vec<&'a crate::database::schema::commit::Commit<'a>>,
    commit_list: Vec<YokedCommit>,
    branch: Option<Arc<str>>,
}

pub async fn handle(
    Extension(repo): Extension<Repository>,
    Extension(db): Extension<Arc<rocksdb::DB>>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    tokio::task::spawn_blocking(move || {
        let repository = crate::database::schema::repository::Repository::open(&db, &*repo)?
            .context("Repository does not exist")?;
        let commits = get_default_branch_commits(&repository, &db)?;
        let commit_list = commits.iter().map(Yoke::get).collect();

        let mut heads = BTreeMap::new();
        for head in repository.get().heads(&db)?.get() {
@@ -45,10 +43,10 @@

        let tags = repository.get().tag_tree(db).fetch_all()?;

        Ok(into_response(&View {
        Ok(into_response(View {
            repo,
            refs: Refs { heads, tags },
            commit_list,
            commit_list: commits,
            branch: None,
        }))
    })
diff --git a/src/methods/repo/tag.rs b/src/methods/repo/tag.rs
index e337d6b..0b7dab4 100644
--- a/src/methods/repo/tag.rs
+++ a/src/methods/repo/tag.rs
@@ -1,13 +1,16 @@
use std::sync::Arc;

use askama::Template;
use axum::{extract::Query, response::Response, Extension};
use axum::{extract::Query, response::IntoResponse, Extension};
use serde::Deserialize;

use crate::{
    git::DetailedTag,
    into_response,
    methods::repo::{Repository, RepositoryPath, Result},
    methods::{
        filters,
        repo::{Repository, RepositoryPath, Result},
    },
    Git,
};

@@ -30,11 +33,11 @@
    Extension(RepositoryPath(repository_path)): Extension<RepositoryPath>,
    Extension(git): Extension<Arc<Git>>,
    Query(query): Query<UriQuery>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    let open_repo = git.repo(repository_path, Some(query.name.clone())).await?;
    let tag = open_repo.tag_info().await?;

    Ok(into_response(&View {
    Ok(into_response(View {
        repo,
        tag,
        branch: Some(query.name),
diff --git a/src/methods/repo/tree.rs b/src/methods/repo/tree.rs
index 1f1ba6c..485c2da 100644
--- a/src/methods/repo/tree.rs
+++ a/src/methods/repo/tree.rs
@@ -1,14 +1,10 @@
use std::{
    fmt::{Display, Formatter},
    sync::Arc,
};

use askama::Template;
use axum::{
    extract::Query,
    response::{IntoResponse, Response},
    Extension,
};
use axum::{extract::Query, response::IntoResponse, Extension};
use serde::Deserialize;

use crate::{
@@ -18,7 +14,7 @@
        filters,
        repo::{ChildPath, Repository, RepositoryPath, Result},
    },
    Git,
    Git, ResponseEither,
};

#[derive(Deserialize)]
@@ -71,7 +67,7 @@
    Extension(ChildPath(child_path)): Extension<ChildPath>,
    Extension(git): Extension<Arc<Git>>,
    Query(query): Query<UriQuery>,
) -> Result<Response> {
) -> Result<impl IntoResponse> {
    let open_repo = git.repo(repository_path, query.branch.clone()).await?;

    Ok(
@@ -79,18 +75,22 @@
            .path(child_path, query.id.as_deref(), !query.raw)
            .await?
        {
            PathDestination::Tree(items) => into_response(&TreeView {
                repo,
                items,
                branch: query.branch.clone(),
                query,
            }),
            PathDestination::File(file) if query.raw => file.content.into_response(),
            PathDestination::File(file) => into_response(&FileView {
                repo,
                file,
                branch: query.branch,
            }),
            PathDestination::Tree(items) => {
                ResponseEither::Left(ResponseEither::Left(into_response(TreeView {
                    repo,
                    items,
                    branch: query.branch.clone(),
                    query,
                })))
            }
            PathDestination::File(file) if query.raw => ResponseEither::Right(file.content),
            PathDestination::File(file) => {
                ResponseEither::Left(ResponseEither::Right(into_response(FileView {
                    repo,
                    file,
                    branch: query.branch,
                })))
            }
        },
    )
}
diff --git a/templates/repo/macros/refs.html b/templates/repo/macros/refs.html
index 705697f..e802355 100644
--- a/templates/repo/macros/refs.html
+++ a/templates/repo/macros/refs.html
@@ -71,6 +71,7 @@

    <tbody>
    {% for commit in commits -%}
    {% set commit = commit.get() %}
    <tr>
        <td>
            <time datetime="{{ commit.committer.time }}" title="{{ commit.committer.time }}">