Skip to main content

miden_protocol/account/component/storage/schema/
map_slot.rs

1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::string::String;
4use alloc::vec::Vec;
5
6use super::super::{InitStorageData, StorageValueName};
7use super::{WordSchema, parse_storage_value_with_schema, validate_description_ascii};
8use crate::Word;
9use crate::account::{StorageMap, StorageMapKey, StorageSlotName};
10use crate::errors::ComponentMetadataError;
11use crate::utils::serde::{
12    ByteReader,
13    ByteWriter,
14    Deserializable,
15    DeserializationError,
16    Serializable,
17};
18
19// MAP SLOT SCHEMA
20// ================================================================================================
21
22/// Describes the schema for a storage map slot.
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct MapSlotSchema {
25    description: Option<String>,
26    default_values: Option<BTreeMap<Word, Word>>,
27    key_schema: WordSchema,
28    value_schema: WordSchema,
29}
30
31impl MapSlotSchema {
32    pub fn new(
33        description: Option<String>,
34        default_values: Option<BTreeMap<Word, Word>>,
35        key_schema: WordSchema,
36        value_schema: WordSchema,
37    ) -> Self {
38        Self {
39            description,
40            default_values,
41            key_schema,
42            value_schema,
43        }
44    }
45
46    pub fn description(&self) -> Option<&String> {
47        self.description.as_ref()
48    }
49
50    /// Builds a [`StorageMap`] from the provided initialization data.
51    ///
52    /// Merges any default values with entries from the init data, validating that the data
53    /// contains map entries (not a direct value or field entries).
54    pub fn try_build_map(
55        &self,
56        init_storage_data: &InitStorageData,
57        slot_name: &StorageSlotName,
58    ) -> Result<StorageMap, ComponentMetadataError> {
59        let mut entries = self.default_values.clone().unwrap_or_default();
60        let slot_prefix = StorageValueName::from_slot_name(slot_name);
61
62        if init_storage_data.slot_value_entry(slot_name).is_some() {
63            return Err(ComponentMetadataError::InvalidInitStorageValue(
64                slot_prefix,
65                "expected a map, got a value".into(),
66            ));
67        }
68        if init_storage_data.has_field_entries_for_slot(slot_name) {
69            return Err(ComponentMetadataError::InvalidInitStorageValue(
70                slot_prefix,
71                "expected a map, got field entries".into(),
72            ));
73        }
74        if let Some(init_entries) = init_storage_data.map_entries(slot_name) {
75            let mut parsed_entries = Vec::with_capacity(init_entries.len());
76            for (raw_key, raw_value) in init_entries.iter() {
77                let key = parse_storage_value_with_schema(&self.key_schema, raw_key, &slot_prefix)?;
78                let value =
79                    parse_storage_value_with_schema(&self.value_schema, raw_value, &slot_prefix)?;
80
81                parsed_entries.push((key, value));
82            }
83
84            for (key, value) in parsed_entries.iter() {
85                entries.insert(*key, *value);
86            }
87        }
88
89        if entries.is_empty() {
90            return Ok(StorageMap::new());
91        }
92
93        StorageMap::with_entries(
94            entries.into_iter().map(|(key, value)| (StorageMapKey::from_raw(key), value)),
95        )
96        .map_err(|err| ComponentMetadataError::StorageMapHasDuplicateKeys(Box::new(err)))
97    }
98
99    pub fn key_schema(&self) -> &WordSchema {
100        &self.key_schema
101    }
102
103    pub fn value_schema(&self) -> &WordSchema {
104        &self.value_schema
105    }
106
107    pub fn default_values(&self) -> Option<BTreeMap<Word, Word>> {
108        self.default_values.clone()
109    }
110
111    pub(super) fn write_into_with_optional_defaults<W: ByteWriter>(
112        &self,
113        target: &mut W,
114        include_defaults: bool,
115    ) {
116        target.write(&self.description);
117        let default_values = if include_defaults {
118            self.default_values.clone()
119        } else {
120            None
121        };
122        target.write(&default_values);
123        self.key_schema.write_into_with_optional_defaults(target, include_defaults);
124        self.value_schema.write_into_with_optional_defaults(target, include_defaults);
125    }
126
127    pub(super) fn validate(&self) -> Result<(), ComponentMetadataError> {
128        if let Some(description) = self.description.as_deref() {
129            validate_description_ascii(description)?;
130        }
131        self.key_schema.validate()?;
132        self.value_schema.validate()?;
133        Ok(())
134    }
135}
136
137impl Serializable for MapSlotSchema {
138    fn write_into<W: ByteWriter>(&self, target: &mut W) {
139        self.write_into_with_optional_defaults(target, true);
140    }
141}
142
143impl Deserializable for MapSlotSchema {
144    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
145        let description = Option::<String>::read_from(source)?;
146        let default_values = Option::<BTreeMap<Word, Word>>::read_from(source)?;
147        let key_schema = WordSchema::read_from(source)?;
148        let value_schema = WordSchema::read_from(source)?;
149        Ok(MapSlotSchema::new(description, default_values, key_schema, value_schema))
150    }
151}