use serde_json::{Value, json};
pub(crate) const SEALED: &str = "$sealed";
#[must_use]
pub fn is_sealed(value: &Value) -> bool {
value
.as_object()
.is_some_and(|o| o.len() == 1 && o.get(SEALED).is_some_and(serde_json::Value::is_string))
}
#[must_use]
pub fn is_sealed_text(text: &str) -> bool {
use base64::Engine;
text.strip_prefix(SEALED)
.and_then(|rest| rest.strip_prefix(':'))
.is_some_and(|encoded| {
base64::engine::general_purpose::STANDARD
.decode(encoded)
.is_ok()
})
}
pub(crate) fn wrap(envelope: &[u8]) -> Value {
use base64::Engine;
json!({ SEALED: base64::engine::general_purpose::STANDARD.encode(envelope) })
}
pub(crate) fn unwrap(value: &Value) -> Option<Vec<u8>> {
use base64::Engine;
if !is_sealed(value) {
return None;
}
let encoded = value.as_object()?.get(SEALED)?.as_str()?;
base64::engine::general_purpose::STANDARD
.decode(encoded)
.ok()
}
pub(crate) fn wrap_text(envelope: &[u8]) -> String {
use base64::Engine;
format!(
"{SEALED}:{}",
base64::engine::general_purpose::STANDARD.encode(envelope)
)
}
pub(crate) fn unwrap_text(text: &str) -> Option<Vec<u8>> {
use base64::Engine;
let encoded = text.strip_prefix(SEALED)?.strip_prefix(':')?;
base64::engine::general_purpose::STANDARD
.decode(encoded)
.ok()
}
pub(crate) enum SealedField<'a> {
Value(&'a mut Value),
Text(&'a mut String),
}
pub(crate) fn payloads(kind: &mut super::RecordKind) -> Vec<SealedField<'_>> {
use super::RecordKind as K;
match kind {
K::RunAdmitted { input, .. } => vec![SealedField::Value(input)],
K::PlanFrozen { plan, .. } => vec![SealedField::Value(plan)],
K::EffectStarted { descriptor, .. } => vec![SealedField::Value(&mut descriptor.args)],
K::EffectDone { output, .. } => vec![SealedField::Value(output)],
K::EffectReconciled { output, detail, .. } => output
.as_mut()
.map(SealedField::Value)
.into_iter()
.chain(detail.as_mut().map(SealedField::Text))
.collect(),
K::GroupSettled { detail, .. } => {
detail.as_mut().map(SealedField::Text).into_iter().collect()
}
K::EffectFailed { error, .. } => vec![SealedField::Text(error)],
K::Note { text } => vec![SealedField::Text(text)],
K::StepStarted { .. }
| K::StepFinished { .. }
| K::CaseBound { .. }
| K::DeadlineRegistered { .. }
| K::DeadlineTransition { .. }
| K::RunSuspended { .. }
| K::BudgetRefused { .. }
| K::BudgetReadmitted { .. }
| K::IdentityBound { .. }
| K::PolicyDenied { .. }
| K::GroupOpened { .. }
| K::StepCompensated { .. }
| K::Released { .. }
| K::RunCancelled { .. }
| K::RunSealed { .. }
| K::BreakGlass { .. }
| K::Swept { .. } => Vec::new(),
}
}