use js_export_macro::js_export;
use miden_client::Word as NativeWord;
use miden_client::account::{
AccountStoragePatch as NativeAccountStoragePatch,
StorageMapPatch as NativeStorageMapPatch,
StorageSlotName,
StorageValuePatch as NativeStorageValuePatch,
};
use miden_protocol::account::StoragePatchOperation as NativeStoragePatchOperation;
use crate::models::word::Word;
use crate::platform::{JsBytes, JsErr};
use crate::utils::{deserialize_from_bytes, serialize_to_bytes};
#[derive(Clone)]
#[js_export]
pub struct AccountStoragePatch(NativeAccountStoragePatch);
#[js_export]
impl AccountStoragePatch {
pub fn serialize(&self) -> JsBytes {
serialize_to_bytes(&self.0)
}
pub fn deserialize(bytes: JsBytes) -> Result<AccountStoragePatch, JsErr> {
deserialize_from_bytes::<NativeAccountStoragePatch>(&bytes).map(Self)
}
#[js_export(js_name = "isEmpty")]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn values(&self) -> Vec<Word> {
self.0
.values()
.filter_map(|(_slot_name, patch)| patch.value())
.map(Into::into)
.collect()
}
#[js_export(js_name = "valueSlots")]
pub fn value_slots(&self) -> Vec<StorageValueSlotPatch> {
self.0
.values()
.map(|(slot_name, patch)| StorageValueSlotPatch {
slot_name: slot_name.clone(),
patch: patch.clone(),
})
.collect()
}
#[js_export(js_name = "mapSlots")]
pub fn map_slots(&self) -> Vec<StorageMapSlotPatch> {
self.0
.maps()
.map(|(slot_name, patch)| StorageMapSlotPatch {
slot_name: slot_name.clone(),
patch: patch.clone(),
})
.collect()
}
}
#[js_export]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum StoragePatchOperation {
Create = 0,
Update = 1,
Remove = 2,
}
const _: () = {
assert!(NativeStoragePatchOperation::Create.as_u8() == StoragePatchOperation::Create as u8);
assert!(NativeStoragePatchOperation::Update.as_u8() == StoragePatchOperation::Update as u8);
assert!(NativeStoragePatchOperation::Remove.as_u8() == StoragePatchOperation::Remove as u8);
};
impl From<NativeStoragePatchOperation> for StoragePatchOperation {
fn from(value: NativeStoragePatchOperation) -> Self {
match value {
NativeStoragePatchOperation::Create => StoragePatchOperation::Create,
NativeStoragePatchOperation::Update => StoragePatchOperation::Update,
NativeStoragePatchOperation::Remove => StoragePatchOperation::Remove,
}
}
}
#[derive(Clone)]
#[js_export]
pub struct StorageValueSlotPatch {
slot_name: StorageSlotName,
patch: NativeStorageValuePatch,
}
#[js_export]
impl StorageValueSlotPatch {
#[js_export(getter, js_name = "slotName")]
pub fn slot_name(&self) -> String {
self.slot_name.as_str().to_string()
}
#[js_export(getter)]
pub fn operation(&self) -> StoragePatchOperation {
self.patch.patch_op().into()
}
#[js_export(getter)]
pub fn value(&self) -> Option<Word> {
self.patch.value().map(Into::into)
}
}
#[derive(Clone)]
#[js_export]
pub struct StorageMapSlotPatch {
slot_name: StorageSlotName,
patch: NativeStorageMapPatch,
}
#[js_export]
impl StorageMapSlotPatch {
#[js_export(getter, js_name = "slotName")]
pub fn slot_name(&self) -> String {
self.slot_name.as_str().to_string()
}
#[js_export(getter)]
pub fn operation(&self) -> StoragePatchOperation {
self.patch.patch_op().into()
}
pub fn entries(&self) -> Vec<StorageMapPatchEntry> {
self.patch
.entries()
.into_iter()
.flat_map(|entries| entries.as_map().iter())
.map(|(key, value)| StorageMapPatchEntry {
key: NativeWord::from(*key).into(),
value: (*value).into(),
})
.collect()
}
}
#[derive(Clone)]
#[js_export]
pub struct StorageMapPatchEntry {
key: Word,
value: Word,
}
#[js_export]
impl StorageMapPatchEntry {
#[js_export(getter)]
pub fn key(&self) -> Word {
self.key.clone()
}
#[js_export(getter)]
pub fn value(&self) -> Word {
self.value.clone()
}
}
impl From<NativeAccountStoragePatch> for AccountStoragePatch {
fn from(patch: NativeAccountStoragePatch) -> Self {
Self(patch)
}
}
impl From<&NativeAccountStoragePatch> for AccountStoragePatch {
fn from(patch: &NativeAccountStoragePatch) -> Self {
Self(patch.clone())
}
}
impl From<AccountStoragePatch> for NativeAccountStoragePatch {
fn from(patch: AccountStoragePatch) -> Self {
patch.0
}
}