use serde::{Deserialize, Serialize};
use super::{require_schema, validate_id};
use crate::{UtcTimestamp, ValidationError};
pub const BUS_LEASE_REQUEST_SCHEMA: &str = "leanctx.hive.bus-lease-request/v1";
pub const BUS_LEASE_RENEW_REQUEST_SCHEMA: &str = "leanctx.hive.bus-lease-renew-request/v1";
pub const BUS_LEASE_SCHEMA: &str = "leanctx.hive.bus-lease/v1";
pub const MAX_BUS_LEASE_MS: u64 = 15 * 60 * 1000;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BusLeaseHolderV1 {
pub agent_id: String,
pub instance_id: String,
pub expires_at: UtcTimestamp,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BusLeaseRequestV1 {
pub schema: String,
pub resource_kind: String,
pub resource_ref: String,
pub vault_id: String,
pub owner_agent_id: String,
pub duration_ms: u64,
}
impl BusLeaseRequestV1 {
pub fn validate(&self) -> Result<(), ValidationError> {
require_schema(&self.schema, BUS_LEASE_REQUEST_SCHEMA)?;
validate_id(&self.resource_kind, "resource_kind")?;
super::validate_hex64(&self.resource_ref, "resource_ref")?;
super::validate_vault_id(&self.vault_id)?;
validate_id(&self.owner_agent_id, "owner_agent_id")?;
validate_duration(self.duration_ms)
}
}
fn validate_duration(duration_ms: u64) -> Result<(), ValidationError> {
if (1..=MAX_BUS_LEASE_MS).contains(&duration_ms) {
Ok(())
} else {
Err(ValidationError::new("duration_ms must be 1-900000"))
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct BusLeaseRenewRequestV1 {
pub schema: String,
pub fencing_token: u64,
pub duration_ms: u64,
}
impl BusLeaseRenewRequestV1 {
pub fn validate(&self) -> Result<(), ValidationError> {
require_schema(&self.schema, BUS_LEASE_RENEW_REQUEST_SCHEMA)?;
validate_duration(self.duration_ms)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct BusLeaseV1 {
pub schema: String,
pub granted: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub lease_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub fencing_token: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expires_at: Option<UtcTimestamp>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub holder: Option<BusLeaseHolderV1>,
pub server_time: UtcTimestamp,
}
impl BusLeaseV1 {
pub fn validate(&self) -> Result<(), ValidationError> {
require_schema(&self.schema, BUS_LEASE_SCHEMA)?;
let granted_fields =
self.lease_id.is_some() && self.fencing_token.is_some() && self.expires_at.is_some();
if self.granted != granted_fields || (self.granted && self.holder.is_some()) {
return Err(ValidationError::new(
"a granted lease has id, token and expiry; a refused one has none",
));
}
Ok(())
}
}