diff --git a/Cargo.lock b/Cargo.lock
@@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
-version = 4
+version = 3
[[package]]
name = "addr2line"
diff --git a/src/methods/index.rs b/src/methods/index.rs
@@ -1,8 +1,12 @@
-use std::{collections::BTreeMap, sync::Arc};
+use std::{cell::RefCell, sync::Arc};
use anyhow::Context;
use askama::Template;
-use axum::{response::IntoResponse, Extension};
+use axum::{
+ response::{IntoResponse, Response},
+ Extension,
+};
+use itertools::{Either, Itertools};
use super::filters;
use crate::{
@@ -12,28 +16,45 @@ use crate::{
#[derive(Template)]
#[template(path = "index.html")]
-pub struct View {
- pub repositories: BTreeMap<Option<String>, Vec<YokedRepository>>,
+pub struct View<
+ 'a,
+ Group: Iterator<Item = (&'a String, &'a YokedRepository)>,
+ GroupIter: Iterator<Item = (&'a str, Group)>,
+> {
+ // this type sig is a necessary evil unfortunately, because askama takes a reference
+ // to the data for rendering.
+ pub repositories: RefCell<Either<GroupIter, std::iter::Empty<(&'a str, Group)>>>,
+}
+
+impl<'a, Group, GroupIter> View<'a, Group, GroupIter>
+where
+ Group: Iterator<Item = (&'a String, &'a YokedRepository)>,
+ GroupIter: Iterator<Item = (&'a str, Group)>,
+{
+ fn take_iter(&self) -> Either<GroupIter, std::iter::Empty<(&'a str, Group)>> {
+ self.repositories.replace(Either::Right(std::iter::empty()))
+ }
}
pub async fn handle(
Extension(db): Extension<Arc<rocksdb::DB>>,
-) -> Result<impl IntoResponse, super::repo::Error> {
- let mut repositories: BTreeMap<Option<String>, Vec<YokedRepository>> = BTreeMap::new();
-
+) -> Result<Response, super::repo::Error> {
let fetched = tokio::task::spawn_blocking(move || Repository::fetch_all(&db))
.await
.context("Failed to join Tokio task")??;
- 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);
- }
-
- Ok(into_response(View { repositories }))
+ // rocksdb returned the keys already ordered for us so group_by is a nice
+ // operation we can use here to avoid writing into a map to group. though,
+ // now that i think about it it might act a little bit strangely when mixing
+ // root repositories and nested repositories. we're going to have to prefix
+ // root repositories with a null byte or something. i'll just leave this here
+ // as a TODO.
+ let repositories = fetched
+ .iter()
+ .group_by(|(k, _)| memchr::memrchr(b'/', k.as_bytes()).map_or("", |idx| &k[..idx]));
+
+ Ok(into_response(View {
+ repositories: Either::Left(repositories.into_iter()).into(),
+ })
+ .into_response())
}
diff --git a/templates/index.html b/templates/index.html
@@ -13,21 +13,21 @@
</thead>
<tbody>
- {%- for (path, repositories) in repositories %}
- {%- if let Some(path) = path %}
+ {%- for (path, repositories) in self.take_iter() %}
+ {%- if !path.is_empty() %}
<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 %}">
+ {% set repository = repository.1.get() %}
+ <tr class="{% if !path.is_empty() %}has-parent{% endif %}">
<td>
- <a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
+ <a href="/{% if !path.is_empty() %}{{ path }}/{% endif %}{{ repository.name }}">
{{- repository.name -}}
</a>
</td>
<td>
- <a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
+ <a href="/{% if !path.is_empty() %}{{ path }}/{% endif %}{{ repository.name }}">
{%- if let Some(description) = repository.description.as_ref() -%}
{{- description -}}
{%- else -%}
@@ -36,14 +36,14 @@
</a>
</td>
<td>
- <a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
+ <a href="/{% if !path.is_empty() %}{{ path }}/{% endif %}{{ repository.name }}">
{%- if let Some(owner) = repository.owner.as_ref() -%}
{{- owner -}}
{%- endif -%}
</a>
</td>
<td>
- <a href="/{% if let Some(path) = path %}{{ path }}/{% endif %}{{ repository.name }}">
+ <a href="/{% if !path.is_empty() %}{{ path }}/{% endif %}{{ repository.name }}">
<time datetime="{{ repository.last_modified|format_time }}" title="{{ repository.last_modified|format_time }}">
{{- repository.last_modified|timeago -}}
</time>