use chrono::{DateTime, Utc};
use serde::de::DeserializeOwned;
use serde_json::{Value, json};
use vti_common::error::AppError;
use super::model::{Namespace, Repo, Resource, RightRow};
pub fn into<T: DeserializeOwned>(value: Value) -> Result<T, AppError> {
let type_name = std::any::type_name::<T>();
serde_json::from_value(value).map_err(|e| {
AppError::Internal(format!(
"a git-ns record does not fit its wire type {type_name}: {e}"
))
})
}
pub fn namespace(ns: &Namespace) -> Value {
let mut v = json!({
"id": ns.id,
"forge": ns.forge,
"owner": ns.owner,
"mode": ns.mode.as_str(),
"state": match ns.state {
super::model::NamespaceState::Pending => "pending",
super::model::NamespaceState::Bound => "bound",
},
});
if let Some(kind) = ns.kind {
v["kind"] = json!(kind.as_str());
}
v
}
pub fn repo_summary(repo: &Repo, owners: &[String]) -> Value {
let mut sync = json!({
"state": repo.sync.state.as_str(),
"drift": repo.sync.drift,
});
if let Some(at) = repo.sync.checked_at {
sync["checkedAt"] = json!(at);
}
let mut v = json!({
"resource": repo.resource,
"visibility": repo.visibility.as_str(),
"state": repo.state.as_str(),
"owners": owners,
"bootstrap": {
"workflow": repo.bootstrap.workflow,
"keyring": repo.bootstrap.keyring,
"variables": repo.bootstrap.variables,
"requiredCheck": repo.bootstrap.required_check,
},
"sync": sync,
});
if let Some(id) = &repo.forge_id {
v["forgeId"] = json!(id);
}
v
}
pub fn right_record(row: &RightRow, resource: &Resource, with_reason: bool) -> Value {
let mut v = json!({
"subject": row.subject,
"right": row.right.as_str(),
"resource": resource.to_string(),
"grantedBy": row.granted_by,
"grantedAt": timestamp(row.granted_at),
});
if let Some(e) = row.expires_at {
v["expiresAt"] = json!(timestamp(e));
}
if with_reason && let Some(r) = &row.reason {
v["reason"] = json!(r);
}
v
}
pub fn timestamp(t: DateTime<Utc>) -> String {
t.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
}
pub fn right_record_full(row: &RightRow, resource: &Resource, with_reason: bool) -> Value {
let mut v = right_record(row, resource, with_reason);
if let Some(bg) = &row.break_glass {
let mut b = json!({
"by": bg.by,
"at": timestamp(bg.at),
"justification": bg.justification,
});
if let Some(e) = bg.effective_at {
b["effectiveAt"] = json!(timestamp(e));
}
if let Some(r) = &bg.ratified_by {
b["ratifiedBy"] = json!(r);
}
if let Some(r) = bg.ratified_at {
b["ratifiedAt"] = json!(timestamp(r));
}
v["breakGlass"] = b;
}
v
}