wsb-daemon 0.248.6265

wsb's daemon: the workbench, the GitHub mirror's schedule and peer continuity, for every workspace on the machine
//! Everything the GitHub mirror holds about repositories and the person,
//! served from the mirror.
//!
//! One shape — `{ "items": […], "sync": { … } }` for a list, `{ "item": …,
//! "sync": { … } }` for a single thing — and the kinds differ only in which
//! scope they name. Nothing here reaches GitHub.

use axum::extract::{Path, State};
use axum::Json;
use serde_json::json;

use crate::web::github::{self, Refusal};
use crate::web::state::WebState;
use wsb_github::Scope;

async fn list(state: &WebState, scope: Scope) -> Result<Json<wsb_github::service::Listed>, Refusal> {
    let service = github::of(state)?.service.clone();
    github::blocking(move || service.list(&scope)).await.map(Json)
}

async fn single(state: &WebState, scope: Scope) -> Result<Json<wsb_github::service::Single>, Refusal> {
    let service = github::of(state)?.service.clone();
    github::blocking(move || service.single(&scope)).await.map(Json)
}

pub async fn list_pulls(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<wsb_github::service::Listed>, Refusal> {
    list(&state, Scope::Pulls(format!("{owner}/{repo}"))).await
}

pub async fn list_releases(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<wsb_github::service::Listed>, Refusal> {
    list(&state, Scope::Releases(format!("{owner}/{repo}"))).await
}

pub async fn list_tags(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<wsb_github::service::Listed>, Refusal> {
    list(&state, Scope::Tags(format!("{owner}/{repo}"))).await
}

pub async fn list_milestones(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<wsb_github::service::Listed>, Refusal> {
    list(&state, Scope::Milestones(format!("{owner}/{repo}"))).await
}

pub async fn list_labels(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<wsb_github::service::Listed>, Refusal> {
    list(&state, Scope::Labels(format!("{owner}/{repo}"))).await
}

pub async fn repository(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<wsb_github::service::Single>, Refusal> {
    single(&state, Scope::Repository(format!("{owner}/{repo}"))).await
}

/// CI for the commit THIS workspace has the repository checked out at.
///
/// Another worktree of the same project may be on another commit, with CI of
/// its own; this page is about the work in front of the person.
pub async fn checks(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<serde_json::Value>, Refusal> {
    let name = format!("{owner}/{repo}");
    let head = github::repositories(&state)
        .await?
        .into_iter()
        .find(|repository| repository.name == name)
        .and_then(|repository| repository.head);
    let Some(commit) = head else {
        // Not checked out here, or with no commit yet: there is no commit to
        // ask about, which is said rather than answered with another's CI.
        return Ok(Json(json!({ "item": null, "sync": wsb_github::Standing::default() })));
    };
    let answer = single(&state, Scope::Checks { repo: name, commit }).await?;
    Ok(Json(serde_json::to_value(answer.0).map_err(|e| {
        github::failure(wsb_github::Failure::Mirror(e.into()))
    })?))
}

pub async fn traffic(
    State(state): State<WebState>,
    Path((owner, repo)): Path<(String, String)>,
) -> Result<Json<wsb_github::service::Single>, Refusal> {
    single(&state, Scope::Traffic(format!("{owner}/{repo}"))).await
}

pub async fn notifications(State(state): State<WebState>) -> Result<Json<wsb_github::service::Listed>, Refusal> {
    list(&state, Scope::Notifications).await
}

pub async fn rate_limit(State(state): State<WebState>) -> Result<Json<wsb_github::service::Single>, Refusal> {
    single(&state, Scope::RateLimit).await
}

/// Every repository this workspace has, with what the mirror knows about
/// each, in one request — the explorer draws forty rows and each wants the
/// same few facts.
pub async fn overview(State(state): State<WebState>) -> Result<Json<serde_json::Value>, Refusal> {
    let service = github::of(&state)?.service.clone();
    let repositories = github::repositories(&state).await?;
    let facts = github::blocking(move || service.overview(&repositories)).await?;
    Ok(Json(json!({ "repositories": facts })))
}

/// Refresh the machine's mirror now, and say what it did.
pub async fn refresh(State(state): State<WebState>) -> Result<Json<wsb_github::sync::SyncReport>, Refusal> {
    super::issues::refresh(&state).await.map(Json)
}