use jacquard_common::types::{cid::Cid, string::Did, tid::Tid};
use crate::types::RepoStatus;
use super::*;
#[derive(Deserialize)]
pub struct GetLatestCommit {
pub did: Did,
}
#[derive(Serialize)]
pub struct GetLatestCommitOutput {
pub cid: Cid,
pub rev: Tid,
}
pub async fn handle(
State(hydrant): State<Hydrant>,
ExtractXrpc(req): ExtractXrpc<GetLatestCommit>,
) -> XrpcResult<Json<GetLatestCommitOutput>> {
let Some(state) = hydrant
.repos
.get(&req.did)
.state()
.await
.map_err(internal_error)?
else {
return Err(xrpc_error(StatusCode::NOT_FOUND, "RepoNotFound", None));
};
// return specific errors for inactive account states
let xrpc_err = match &state.status {
RepoStatus::Takendown => Some(("RepoTakendown", None)),
RepoStatus::Suspended => Some(("RepoSuspended", None)),
RepoStatus::Deactivated => Some(("RepoDeactivated", None)),
RepoStatus::Deleted => Some(("RepoNotFound", Some("deleted".into()))),
_ => None,
};
if let Some((code, msg)) = xrpc_err {
return Err(xrpc_error(StatusCode::FORBIDDEN, code, msg));
}
// return whatever we last recorded; if we haven't synced at all yet, we have nothing to give
let Some(commit) = state.root else {
return Err(xrpc_error(
StatusCode::NOT_FOUND,
"RepoNotFound",
Some("repo still backfilling".into()),
));
};
let Some(atp_commit) = commit.into_atp_commit(req.did) else {
return Err(internal_error("repo needs migration"));
};
let commit_cid = atp_commit.to_cid().map_err(internal_error)?;
Ok(Json(GetLatestCommitOutput {
cid: Cid::ipld(commit_cid),
rev: atp_commit.rev,
}))
}