use std::sync::Arc; use poem::{ IntoResponse, Result, handler, web::{Data, Query}, }; use sailfish::TemplateOnce; use serde::Deserialize; use crate::{App, repos::Summary}; #[derive(Deserialize)] pub struct PageQuery { page: Option, } #[derive(TemplateOnce)] #[template(path = "pages/index.stpl.html")] struct IndexPage { repos: Vec, page: usize, pages: usize, } #[handler] pub async fn handler( app: Data<&Arc>, Query(query): Query, ) -> Result { let page = query.page.unwrap_or(1).max(1); let page_size = app.config.page_size as usize; let listing = app .blocking(move |store| { let names = store.public_names()?; let pages = names.len().div_ceil(page_size).max(1); if page > pages { return Ok(None); } let mut repos = Vec::new(); for name in names.iter().skip((page - 1) * page_size).take(page_size) { match store.open(name) { Ok(Some(repo)) => repos.push(repo.summary()), // Unpublished since the listing was taken. Ok(None) => {} // One broken repository must not take the whole site // down; it is reported and left out. Err(err) => tracing::warn!( repo = %name, error = format!("{err:#}"), "leaving an unopenable repository out of the index" ), } } Ok(Some(IndexPage { repos, page, pages })) }) .await?; let index = super::found(listing)?; Ok(super::page(&app, "Repositories", index)?) }