use std::sync::Arc;
use axum::http::StatusCode;
use axum::Json;
use super::handlers::sessions::ErrorResponse;
use super::state::WebState;
pub struct GitHub {
pub prj: wsb_prj::Prj,
pub service: wsb_github::Service,
}
impl GitHub {
pub fn open() -> Result<Arc<Self>, String> {
let prj = wsb_prj::Prj::from_env().map_err(|e| e.to_string())?;
let mirror = wsb_github::Mirror::of(&prj).map_err(|e| format!("{e:#}"))?;
Ok(Arc::new(Self {
prj,
service: wsb_github::Service::new(mirror),
}))
}
}
pub type Refusal = (StatusCode, Json<ErrorResponse>);
fn refusal(status: StatusCode, error: impl Into<String>) -> Refusal {
(status, Json(ErrorResponse { error: error.into() }))
}
pub fn of(state: &WebState) -> Result<&GitHub, Refusal> {
state
.github
.as_ref()
.map(|github| github.as_ref())
.map_err(|reason| refusal(StatusCode::SERVICE_UNAVAILABLE, format!("GitHub is unavailable: {reason}")))
}
pub fn client() -> Result<wsb_github::GitHubClient, Refusal> {
wsb_github::token::resolve()
.map(|token| wsb_github::GitHubClient::new(token.value()))
.map_err(|no_token| refusal(StatusCode::UNAUTHORIZED, no_token.to_string()))
}
pub fn failure(failure: wsb_github::Failure) -> Refusal {
let status = match &failure {
wsb_github::Failure::Invalid(_) => StatusCode::BAD_REQUEST,
wsb_github::Failure::NoToken(_) => StatusCode::UNAUTHORIZED,
other if other.is_authorization() => StatusCode::UNAUTHORIZED,
wsb_github::Failure::GitHub(_) => StatusCode::BAD_GATEWAY,
wsb_github::Failure::Mirror(_) => StatusCode::INTERNAL_SERVER_ERROR,
};
let message = match &failure {
wsb_github::Failure::NoToken(message) => message.clone(),
_ if status == StatusCode::UNAUTHORIZED => {
"GitHub refused the token: it is invalid or lacks the access this needs".to_string()
}
other => other.to_string(),
};
refusal(status, message)
}
pub fn repositories(state: &WebState) -> Result<Vec<wsb_github::checkout::Repository>, Refusal> {
wsb_github::checkout::repositories(&state.workspace_root)
.map_err(|e| refusal(StatusCode::INTERNAL_SERVER_ERROR, format!("{e:#}")))
}
pub fn spawn_keeper(github: Arc<GitHub>) {
tokio::spawn(async move {
let prj = github.prj.clone();
let mirror = github.service.mirror().clone();
wsb_github::sync::keep_current(prj, mirror, |report| {
for (scope, error) in &report.failed {
tracing::debug!(%scope, %error, "a GitHub scope could not be refreshed");
}
for problem in &report.problems {
tracing::debug!(%problem, "what to mirror could not be found out");
}
})
.await;
});
}