use serde::{Deserialize, Deserializer, Serialize};
use super::{ContractVersion, StateLifetime, VNextError};
mod inputs;
pub use inputs::{ProgramCheckpointInputs, PROGRAM_CHECKPOINT_INPUTS_VERSION};
pub const STATE_CHECKPOINT_CONTRACT_VERSION: ContractVersion = ContractVersion::new(1, 0);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum CheckpointInputDependency {
ExactTokenPrefix,
EntireTokenInput,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum StateCheckpointContents {
PrefixPositions,
BoundaryValue,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub struct StateCheckpointContract {
contract_version: ContractVersion,
contents: StateCheckpointContents,
input_dependency: CheckpointInputDependency,
}
#[derive(Deserialize)]
#[serde(deny_unknown_fields)]
struct StateCheckpointContractWire {
contract_version: ContractVersion,
contents: StateCheckpointContents,
input_dependency: CheckpointInputDependency,
}
impl<'de> Deserialize<'de> for StateCheckpointContract {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let wire = StateCheckpointContractWire::deserialize(deserializer)?;
if wire.contract_version != STATE_CHECKPOINT_CONTRACT_VERSION {
return Err(serde::de::Error::custom(format!(
"state checkpoint contract version {} is unsupported",
wire.contract_version
)));
}
Ok(Self::new(wire.contents, wire.input_dependency))
}
}
impl StateCheckpointContract {
pub const fn new(
contents: StateCheckpointContents,
input_dependency: CheckpointInputDependency,
) -> Self {
Self {
contract_version: STATE_CHECKPOINT_CONTRACT_VERSION,
contents,
input_dependency,
}
}
pub const fn contract_version(&self) -> ContractVersion {
self.contract_version
}
pub const fn contents(&self) -> StateCheckpointContents {
self.contents
}
pub const fn input_dependency(&self) -> CheckpointInputDependency {
self.input_dependency
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
pub enum StateCheckpointCapability {
#[default]
Unsupported,
CompletedBoundary(StateCheckpointContract),
}
impl StateCheckpointCapability {
pub const fn is_unsupported(&self) -> bool {
matches!(self, Self::Unsupported)
}
pub(super) fn validate_lifetime(self, lifetime: StateLifetime) -> Result<(), VNextError> {
if !self.is_unsupported() && lifetime != StateLifetime::Sequence {
return Err(VNextError::InvalidExecutionPlan {
reason: "checkpoint contracts currently support only Sequence state; Request/Step state requires a separate closure contract".to_owned(),
});
}
Ok(())
}
}
#[cfg(test)]
mod tests;