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(-)
@@ -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(),
}
}
@@ -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>
@@ -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 }}">
@@ -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 {
@@ -1,7 +1,14 @@
#![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()
@@ -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 {
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 }))
}
@@ -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,
@@ -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,
@@ -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,
@@ -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,
@@ -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,
@@ -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,
}))
})
@@ -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),
@@ -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,
})))
}
},
)
}
@@ -71,6 +71,7 @@
<tbody>
{% for commit in commits -%}
{% set commit = commit.get() %}
<tr>
<td>
<time datetime="{{ commit.committer.time }}" title="{{ commit.committer.time }}">