use super::slot_patch::MergeOutcome;
use crate::Word;
use crate::account::{StoragePatchOperation, StorageSlotName};
use crate::errors::AccountPatchError;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StorageValuePatch {
Create { value: Word },
Update { value: Word },
Remove,
}
impl StorageValuePatch {
const CREATE: u8 = 0;
const UPDATE: u8 = 1;
const REMOVE: u8 = 2;
pub fn value(&self) -> Option<Word> {
match self {
StorageValuePatch::Create { value } | StorageValuePatch::Update { value } => {
Some(*value)
},
StorageValuePatch::Remove => None,
}
}
pub fn patch_op(&self) -> StoragePatchOperation {
match self {
StorageValuePatch::Create { .. } => StoragePatchOperation::Create,
StorageValuePatch::Update { .. } => StoragePatchOperation::Update,
StorageValuePatch::Remove => StoragePatchOperation::Remove,
}
}
pub(super) fn committed_value(&self) -> Word {
self.value().unwrap_or_default()
}
fn compact_value(value: &Word) -> Option<&Word> {
(!value.is_empty()).then_some(value)
}
pub(super) fn merge(
&mut self,
slot_name: &StorageSlotName,
other: Self,
) -> Result<MergeOutcome, AccountPatchError> {
match (self, other) {
(StorageValuePatch::Create { .. }, StorageValuePatch::Create { .. }) => {
return Err(AccountPatchError::StoragePatchMergeDoubleCreate(slot_name.clone()));
},
(
StorageValuePatch::Create { value: current },
StorageValuePatch::Update { value: incoming },
) => *current = incoming,
(StorageValuePatch::Create { .. }, StorageValuePatch::Remove) => {
return Ok(MergeOutcome::Remove);
},
(StorageValuePatch::Update { .. }, StorageValuePatch::Create { .. }) => {
return Err(AccountPatchError::StoragePatchMergeCreateAfterUpdate(
slot_name.clone(),
));
},
(
StorageValuePatch::Update { value: current },
StorageValuePatch::Update { value: incoming },
) => *current = incoming,
(current @ StorageValuePatch::Update { .. }, StorageValuePatch::Remove) => {
*current = StorageValuePatch::Remove
},
(current @ StorageValuePatch::Remove, incoming @ StorageValuePatch::Create { .. }) => {
*current = incoming;
},
(StorageValuePatch::Remove, StorageValuePatch::Update { .. }) => {
return Err(AccountPatchError::StoragePatchMergeUpdateAfterRemove(
slot_name.clone(),
));
},
(StorageValuePatch::Remove, StorageValuePatch::Remove) => {
return Err(AccountPatchError::StoragePatchMergeDoubleRemove(slot_name.clone()));
},
}
Ok(MergeOutcome::Keep)
}
}
impl Serializable for StorageValuePatch {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
match self {
StorageValuePatch::Create { value } => {
target.write_u8(Self::CREATE);
target.write(Self::compact_value(value));
},
StorageValuePatch::Update { value } => {
target.write_u8(Self::UPDATE);
target.write(Self::compact_value(value));
},
StorageValuePatch::Remove => {
target.write_u8(Self::REMOVE);
},
}
}
fn get_size_hint(&self) -> usize {
let tag_size = 0u8.get_size_hint();
match self {
StorageValuePatch::Create { value } | StorageValuePatch::Update { value } => {
tag_size + Self::compact_value(value).get_size_hint()
},
StorageValuePatch::Remove => tag_size,
}
}
}
impl Deserializable for StorageValuePatch {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
match source.read_u8()? {
Self::CREATE => Ok(Self::Create {
value: source.read::<Option<Word>>()?.unwrap_or_default(),
}),
Self::UPDATE => Ok(Self::Update {
value: source.read::<Option<Word>>()?.unwrap_or_default(),
}),
Self::REMOVE => Ok(Self::Remove),
other => Err(DeserializationError::InvalidValue(format!(
"unknown storage value patch variant {other}"
))),
}
}
}