1
- use std:: { collections :: BTreeMap , sync:: Arc } ;
1
+ use std:: { cell :: RefCell , sync:: Arc } ;
2
2
3
3
use anyhow:: Context ;
4
4
use askama:: Template ;
5
- use axum:: { response:: IntoResponse , Extension } ;
5
+ use axum:: {
6
+ response:: { IntoResponse , Response } ,
7
+ Extension ,
8
+ } ;
9
+ use itertools:: { Either , Itertools } ;
6
10
7
11
use super :: filters;
8
12
use crate :: {
@@ -12,28 +16,45 @@ use crate::{
12
16
13
17
#[ derive( Template ) ]
14
18
#[ template( path = "index.html" ) ]
15
- pub struct View {
16
- pub repositories : BTreeMap < Option < String > , Vec < YokedRepository > > ,
19
+ pub struct View <
20
+ ' a ,
21
+ Group : Iterator < Item = ( & ' a String , & ' a YokedRepository ) > ,
22
+ GroupIter : Iterator < Item = ( & ' a str , Group ) > ,
23
+ > {
24
+ // this type sig is a necessary evil unfortunately, because askama takes a reference
25
+ // to the data for rendering.
26
+ pub repositories : RefCell < Either < GroupIter , std:: iter:: Empty < ( & ' a str , Group ) > > > ,
27
+ }
28
+
29
+ impl < ' a , Group , GroupIter > View < ' a , Group , GroupIter >
30
+ where
31
+ Group : Iterator < Item = ( & ' a String , & ' a YokedRepository ) > ,
32
+ GroupIter : Iterator < Item = ( & ' a str , Group ) > ,
33
+ {
34
+ fn take_iter ( & self ) -> Either < GroupIter , std:: iter:: Empty < ( & ' a str , Group ) > > {
35
+ self . repositories . replace ( Either :: Right ( std:: iter:: empty ( ) ) )
36
+ }
17
37
}
18
38
19
39
pub async fn handle (
20
40
Extension ( db) : Extension < Arc < rocksdb:: DB > > ,
21
- ) -> Result < impl IntoResponse , super :: repo:: Error > {
22
- let mut repositories: BTreeMap < Option < String > , Vec < YokedRepository > > = BTreeMap :: new ( ) ;
23
-
41
+ ) -> Result < Response , super :: repo:: Error > {
24
42
let fetched = tokio:: task:: spawn_blocking ( move || Repository :: fetch_all ( & db) )
25
43
. await
26
44
. context ( "Failed to join Tokio task" ) ??;
27
45
28
- for ( k, v) in fetched {
29
- // TODO: fixme
30
- let mut split: Vec < _ > = k. split ( '/' ) . collect ( ) ;
31
- split. pop ( ) ;
32
- let key = Some ( split. join ( "/" ) ) . filter ( |v| !v. is_empty ( ) ) ;
33
-
34
- let k = repositories. entry ( key) . or_default ( ) ;
35
- k. push ( v) ;
36
- }
37
-
38
- Ok ( into_response ( View { repositories } ) )
46
+ // rocksdb returned the keys already ordered for us so group_by is a nice
47
+ // operation we can use here to avoid writing into a map to group. though,
48
+ // now that i think about it it might act a little bit strangely when mixing
49
+ // root repositories and nested repositories. we're going to have to prefix
50
+ // root repositories with a null byte or something. i'll just leave this here
51
+ // as a TODO.
52
+ let repositories = fetched
53
+ . iter ( )
54
+ . group_by ( |( k, _) | memchr:: memrchr ( b'/' , k. as_bytes ( ) ) . map_or ( "" , |idx| & k[ ..idx] ) ) ;
55
+
56
+ Ok ( into_response ( View {
57
+ repositories : Either :: Left ( repositories. into_iter ( ) ) . into ( ) ,
58
+ } )
59
+ . into_response ( ) )
39
60
}
0 commit comments