//! What the appview answers from its index. use crate::{App, AppError}; use axum::Json; use axum::extract::{Query, State}; use axum::http::StatusCode; use serde::Deserialize; use serde_json::{Value, json}; use std::sync::Arc; fn index(app: &App) -> Result<&crate::index::Index, AppError> { app.index .as_deref() .ok_or_else(|| AppError(StatusCode::NOT_FOUND, "this server keeps no index".into())) } #[derive(Deserialize)] pub struct ForgeParams { repo: String, key: String, limit: Option, cursor: Option, } /// The pulls, issues, comments and state changes about a repository: the /// one its owner made with `key`, whatever it has been named. A page at a /// time, by URI. pub async fn get_forge( State(app): State>, Query(p): Query, ) -> Result, AppError> { let did = knotless::forge::parse_uri(&p.repo) .map_err(|e| AppError(StatusCode::BAD_REQUEST, e.to_string()))? .0; let mut entries = index(&app)? .forge(did, &p.key) .map_err(anyhow::Error::from) .map_err(AppError::internal)?; entries.sort_by(|a, b| a.uri.cmp(&b.uri)); let after = p.cursor.unwrap_or_default(); let limit = p.limit.unwrap_or(500).clamp(1, 1000); let mut page: Vec<_> = entries.into_iter().filter(|e| e.uri > after).collect(); let more = page.len() > limit; page.truncate(limit); let mut out = json!({ "entries": page }); if more { out["cursor"] = page[limit - 1].uri.clone().into(); } Ok(Json(out)) } #[derive(Deserialize)] pub struct ListParams { q: Option, limit: Option, cursor: Option, } /// Repositories, newest first, matching `q` by name or description, a /// page at a time. pub async fn list_repos( State(app): State>, Query(p): Query, ) -> Result, AppError> { let limit = p.limit.unwrap_or(50).clamp(1, 500); let (repos, cursor) = index(&app)? .repos_page( p.q.as_deref().unwrap_or_default(), limit, p.cursor.as_deref(), ) .map_err(anyhow::Error::from) .map_err(AppError::internal)?; let mut out = json!({ "repos": repos }); if let Some(cursor) = cursor { out["cursor"] = cursor.into(); } Ok(Json(out)) }