miden_objects/account/component/template/storage/
entry_content.rs

1use alloc::boxed::Box;
2use alloc::collections::BTreeSet;
3use alloc::string::{String, ToString};
4use alloc::vec::Vec;
5use core::iter;
6
7use super::placeholder::{PlaceholderTypeRequirement, TEMPLATE_REGISTRY, TemplateType};
8use super::{
9    FieldIdentifier,
10    InitStorageData,
11    MapEntry,
12    StorageValueName,
13    TemplateRequirementsIter,
14};
15use crate::account::StorageMap;
16use crate::account::component::template::AccountComponentTemplateError;
17use crate::utils::{ByteReader, ByteWriter, Deserializable, DeserializationError, Serializable};
18use crate::{Felt, FieldElement, Word};
19
20// WORDS
21// ================================================================================================
22
23/// Defines how a word is represented within the component's storage description.
24///
25/// Each word representation can be:
26/// - A template that defines a type but does not carry a value.
27/// - A predefined value that may contain a hardcoded word or a mix of fixed and templated felts.
28#[derive(Debug, Clone, PartialEq, Eq)]
29#[allow(clippy::large_enum_variant)]
30pub enum WordRepresentation {
31    /// A templated value that serves as a placeholder for instantiation.
32    ///
33    /// This variant defines a type but does not store a value. The actual value is provided at the
34    /// time of instantiation. The name is required to identify this template externally.
35    Template {
36        /// The type associated with this templated word.
37        r#type: TemplateType,
38        identifier: FieldIdentifier,
39    },
40
41    /// A predefined value that can be used directly within storage.
42    ///
43    /// This variant may contain either a fully hardcoded word or a structured set of felts, some
44    /// of which may themselves be templates.
45    Value {
46        identifier: Option<FieldIdentifier>,
47        /// The 4-felt representation of the stored word.
48        value: [FeltRepresentation; 4],
49    },
50}
51
52impl WordRepresentation {
53    /// Constructs a new `Template` variant.
54    pub fn new_template(r#type: TemplateType, identifier: FieldIdentifier) -> Self {
55        WordRepresentation::Template { r#type, identifier }
56    }
57
58    /// Constructs a new `Value` variant.
59    pub fn new_value(
60        value: impl Into<[FeltRepresentation; 4]>,
61        identifier: Option<FieldIdentifier>,
62    ) -> Self {
63        WordRepresentation::Value { identifier, value: value.into() }
64    }
65
66    /// Sets the description of the [`WordRepresentation`] and returns `self`.
67    pub fn with_description(self, description: impl Into<String>) -> Self {
68        match self {
69            WordRepresentation::Template { r#type, identifier } => WordRepresentation::Template {
70                r#type,
71                identifier: FieldIdentifier {
72                    name: identifier.name,
73                    description: Some(description.into()),
74                },
75            },
76            WordRepresentation::Value { identifier, value } => WordRepresentation::Value {
77                identifier: identifier.map(|id| FieldIdentifier {
78                    name: id.name,
79                    description: Some(description.into()),
80                }),
81                value,
82            },
83        }
84    }
85
86    /// Returns the name associated with the word representation.
87    /// - For the `Template` variant, it always returns a reference to the name.
88    /// - For the `Value` variant, it returns `Some` if a name is present, or `None` otherwise.
89    pub fn name(&self) -> Option<&StorageValueName> {
90        match self {
91            WordRepresentation::Template { identifier, .. } => Some(&identifier.name),
92            WordRepresentation::Value { identifier, .. } => identifier.as_ref().map(|id| &id.name),
93        }
94    }
95
96    /// Returns the description associated with the word representation.
97    /// Both variants store an `Option<String>`, which is converted to an `Option<&str>`.
98    pub fn description(&self) -> Option<&str> {
99        match self {
100            WordRepresentation::Template { identifier, .. } => identifier.description.as_deref(),
101            WordRepresentation::Value { identifier, .. } => {
102                identifier.as_ref().and_then(|id| id.description.as_deref())
103            },
104        }
105    }
106
107    /// Returns the type name.
108    pub fn word_type(&self) -> TemplateType {
109        match self {
110            WordRepresentation::Template { r#type, .. } => r#type.clone(),
111            WordRepresentation::Value { .. } => TemplateType::native_word(),
112        }
113    }
114
115    /// Returns the value (an array of 4 `FeltRepresentation`s) if this is a `Value`
116    /// variant; otherwise, returns `None`.
117    pub fn value(&self) -> Option<&[FeltRepresentation; 4]> {
118        match self {
119            WordRepresentation::Value { value, .. } => Some(value),
120            WordRepresentation::Template { .. } => None,
121        }
122    }
123
124    /// Returns an iterator over the word's placeholders.
125    ///
126    /// For [`WordRepresentation::Value`], it corresponds to the inner iterators (since inner
127    /// elements can be templated as well).
128    /// For [`WordRepresentation::Template`] it returns the words's placeholder requirements
129    /// as defined.
130    pub fn template_requirements(
131        &self,
132        placeholder_prefix: StorageValueName,
133    ) -> TemplateRequirementsIter<'_> {
134        let placeholder_key =
135            placeholder_prefix.with_suffix(self.name().unwrap_or(&StorageValueName::empty()));
136        match self {
137            WordRepresentation::Template { identifier, r#type } => Box::new(iter::once((
138                placeholder_key,
139                PlaceholderTypeRequirement {
140                    description: identifier.description.clone(),
141                    r#type: r#type.clone(),
142                },
143            ))),
144            WordRepresentation::Value { value, .. } => Box::new(
145                value
146                    .iter()
147                    .flat_map(move |felt| felt.template_requirements(placeholder_key.clone())),
148            ),
149        }
150    }
151
152    /// Attempts to convert the [WordRepresentation] into a [Word].
153    ///
154    /// If the representation is a template, the value is retrieved from
155    /// `init_storage_data`, identified by its key. If any of the inner elements
156    /// within the value are a template, they are retrieved in the same way.
157    pub(crate) fn try_build_word(
158        &self,
159        init_storage_data: &InitStorageData,
160        placeholder_prefix: StorageValueName,
161    ) -> Result<Word, AccountComponentTemplateError> {
162        match self {
163            WordRepresentation::Template { identifier, r#type } => {
164                let placeholder_path = placeholder_prefix.with_suffix(&identifier.name);
165                let maybe_value = init_storage_data.get(&placeholder_path);
166                if let Some(value) = maybe_value {
167                    let parsed_value = TEMPLATE_REGISTRY
168                        .try_parse_word(r#type, value)
169                        .map_err(AccountComponentTemplateError::StorageValueParsingError)?;
170
171                    Ok(parsed_value)
172                } else {
173                    Err(AccountComponentTemplateError::PlaceholderValueNotProvided(
174                        placeholder_path,
175                    ))
176                }
177            },
178            WordRepresentation::Value { value, identifier } => {
179                let mut result = [Felt::ZERO; 4];
180
181                for (index, felt_repr) in value.iter().enumerate() {
182                    let placeholder = placeholder_prefix.clone().with_suffix(
183                        identifier
184                            .as_ref()
185                            .map(|id| &id.name)
186                            .unwrap_or(&StorageValueName::empty()),
187                    );
188                    result[index] = felt_repr.try_build_felt(init_storage_data, placeholder)?;
189                }
190                // SAFETY: result is guaranteed to have all its 4 indices rewritten
191                Ok(Word::from(result))
192            },
193        }
194    }
195
196    /// Validates that the defined type exists and all the inner felt types exist as well
197    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
198        // Check that type exists in registry
199        let type_exists = TEMPLATE_REGISTRY.contains_word_type(&self.word_type());
200        if !type_exists {
201            return Err(AccountComponentTemplateError::InvalidType(
202                self.word_type().to_string(),
203                "Word".into(),
204            ));
205        }
206
207        if let Some(felts) = self.value() {
208            for felt in felts {
209                felt.validate()?;
210            }
211        }
212
213        Ok(())
214    }
215}
216
217impl Serializable for WordRepresentation {
218    fn write_into<W: ByteWriter>(&self, target: &mut W) {
219        match self {
220            WordRepresentation::Template { identifier, r#type } => {
221                target.write_u8(0);
222                target.write(identifier);
223                target.write(r#type);
224            },
225            WordRepresentation::Value { identifier, value } => {
226                target.write_u8(1);
227                target.write(identifier);
228                target.write(value);
229            },
230        }
231    }
232}
233
234impl Deserializable for WordRepresentation {
235    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
236        let tag = source.read_u8()?;
237        match tag {
238            0 => {
239                let identifier = FieldIdentifier::read_from(source)?;
240                let r#type = TemplateType::read_from(source)?;
241                Ok(WordRepresentation::Template { identifier, r#type })
242            },
243            1 => {
244                let identifier = Option::<FieldIdentifier>::read_from(source)?;
245                let value = <[FeltRepresentation; 4]>::read_from(source)?;
246                Ok(WordRepresentation::Value { identifier, value })
247            },
248            other => Err(DeserializationError::InvalidValue(format!(
249                "unknown tag '{other}' for WordRepresentation"
250            ))),
251        }
252    }
253}
254
255impl From<[FeltRepresentation; 4]> for WordRepresentation {
256    fn from(value: [FeltRepresentation; 4]) -> Self {
257        WordRepresentation::new_value(value, Option::<FieldIdentifier>::None)
258    }
259}
260
261impl From<[Felt; 4]> for WordRepresentation {
262    fn from(value: [Felt; 4]) -> Self {
263        WordRepresentation::new_value(
264            value.map(FeltRepresentation::from),
265            Option::<FieldIdentifier>::None,
266        )
267    }
268}
269
270// FELTS
271// ================================================================================================
272
273/// Supported element representations for a component's storage entries.
274///
275/// Each felt element in a storage entry can either be:
276/// - A concrete value that holds a predefined felt.
277/// - A template that specifies the type of felt expected, with the actual value to be provided
278///   later.
279#[derive(Debug, Clone, PartialEq, Eq)]
280pub enum FeltRepresentation {
281    /// A concrete felt value.
282    ///
283    /// This variant holds a felt that is part of the component's storage.
284    /// The optional name allows for identification, and the description offers additional context.
285    Value {
286        /// An optional identifier for this felt value.
287        /// An optional explanation of the felt's purpose.
288        identifier: Option<FieldIdentifier>,
289        /// The actual felt value.
290        value: Felt,
291    },
292
293    /// A templated felt element.
294    ///
295    /// This variant specifies the expected type of the felt without providing a concrete value.
296    /// The name is required to uniquely identify the template, and an optional description can
297    /// further clarify its intended use.
298    Template {
299        /// The expected type for this felt element.
300        r#type: TemplateType,
301        /// A unique name for the felt template.
302        /// An optional description that explains the purpose of this template.
303        identifier: FieldIdentifier,
304    },
305}
306
307impl FeltRepresentation {
308    /// Creates a new [`FeltRepresentation::Value`] variant.
309    pub fn new_value(value: impl Into<Felt>, name: Option<StorageValueName>) -> Self {
310        FeltRepresentation::Value {
311            value: value.into(),
312            identifier: name.map(FieldIdentifier::with_name),
313        }
314    }
315
316    /// Creates a new [`FeltRepresentation::Template`] variant.
317    ///
318    /// The name will be used for identification at the moment of instantiating the componentn.
319    pub fn new_template(r#type: TemplateType, name: StorageValueName) -> Self {
320        FeltRepresentation::Template {
321            r#type,
322            identifier: FieldIdentifier::with_name(name),
323        }
324    }
325
326    /// Sets the description of the [`FeltRepresentation`] and returns `self`.
327    pub fn with_description(self, description: impl Into<String>) -> Self {
328        match self {
329            FeltRepresentation::Template { r#type, identifier } => FeltRepresentation::Template {
330                r#type,
331                identifier: FieldIdentifier {
332                    name: identifier.name,
333                    description: Some(description.into()),
334                },
335            },
336            FeltRepresentation::Value { identifier, value } => FeltRepresentation::Value {
337                identifier: identifier.map(|id| FieldIdentifier {
338                    name: id.name,
339                    description: Some(description.into()),
340                }),
341                value,
342            },
343        }
344    }
345
346    /// Returns the felt type.
347    pub fn felt_type(&self) -> TemplateType {
348        match self {
349            FeltRepresentation::Template { r#type, .. } => r#type.clone(),
350            FeltRepresentation::Value { .. } => TemplateType::native_felt(),
351        }
352    }
353
354    /// Attempts to convert the [FeltRepresentation] into a [Felt].
355    ///
356    /// If the representation is a template, the value is retrieved from `init_storage_data`,
357    /// identified by its key. Otherwise, the returned value is just the inner element.
358    pub(crate) fn try_build_felt(
359        &self,
360        init_storage_data: &InitStorageData,
361        placeholder_prefix: StorageValueName,
362    ) -> Result<Felt, AccountComponentTemplateError> {
363        match self {
364            FeltRepresentation::Template { identifier, r#type } => {
365                let placeholder_key = placeholder_prefix.with_suffix(&identifier.name);
366                let raw_value = init_storage_data.get(&placeholder_key).ok_or(
367                    AccountComponentTemplateError::PlaceholderValueNotProvided(placeholder_key),
368                )?;
369
370                Ok(TEMPLATE_REGISTRY
371                    .try_parse_felt(r#type, raw_value)
372                    .map_err(AccountComponentTemplateError::StorageValueParsingError)?)
373            },
374            FeltRepresentation::Value { value, .. } => Ok(*value),
375        }
376    }
377
378    /// Returns an iterator over the felt's template.
379    ///
380    /// For [`FeltRepresentation::Value`], these is an empty set; for
381    /// [`FeltRepresentation::Template`] it returns the felt's placeholder key based on the
382    /// felt's name within the component description.
383    pub fn template_requirements(
384        &self,
385        placeholder_prefix: StorageValueName,
386    ) -> TemplateRequirementsIter<'_> {
387        match self {
388            FeltRepresentation::Template { identifier, r#type } => Box::new(iter::once((
389                placeholder_prefix.with_suffix(&identifier.name),
390                PlaceholderTypeRequirement {
391                    description: identifier.description.clone(),
392                    r#type: r#type.clone(),
393                },
394            ))),
395            _ => Box::new(iter::empty()),
396        }
397    }
398
399    /// Validates that the defined Felt type exists
400    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
401        // Check that type exists in registry
402        let type_exists = TEMPLATE_REGISTRY.contains_felt_type(&self.felt_type());
403        if !type_exists {
404            return Err(AccountComponentTemplateError::InvalidType(
405                self.felt_type().to_string(),
406                "Felt".into(),
407            ));
408        }
409        Ok(())
410    }
411}
412
413impl From<Felt> for FeltRepresentation {
414    fn from(value: Felt) -> Self {
415        FeltRepresentation::new_value(value, Option::<StorageValueName>::None)
416    }
417}
418
419impl Default for FeltRepresentation {
420    fn default() -> Self {
421        FeltRepresentation::new_value(Felt::default(), Option::<StorageValueName>::None)
422    }
423}
424
425impl Serializable for FeltRepresentation {
426    fn write_into<W: ByteWriter>(&self, target: &mut W) {
427        match self {
428            FeltRepresentation::Value { identifier, value } => {
429                target.write_u8(0);
430                target.write(identifier);
431                target.write(value);
432            },
433            FeltRepresentation::Template { identifier, r#type } => {
434                target.write_u8(1);
435                target.write(identifier);
436                target.write(r#type);
437            },
438        }
439    }
440}
441
442impl Deserializable for FeltRepresentation {
443    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
444        let tag = source.read_u8()?;
445        match tag {
446            0 => {
447                let identifier = Option::<FieldIdentifier>::read_from(source)?;
448                let value = Felt::read_from(source)?;
449                Ok(FeltRepresentation::Value { value, identifier })
450            },
451            1 => {
452                let identifier = FieldIdentifier::read_from(source)?;
453                let r#type = TemplateType::read_from(source)?;
454                Ok(FeltRepresentation::Template { r#type, identifier })
455            },
456            other => Err(DeserializationError::InvalidValue(format!(
457                "unknown tag '{other}' for FeltRepresentation"
458            ))),
459        }
460    }
461}
462
463// MAP REPRESENTATION
464// ================================================================================================
465
466/// Supported map representations for a component's storage entries.
467#[derive(Debug, Clone, PartialEq, Eq)]
468#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
469pub struct MapRepresentation {
470    /// The human-readable name of the map slot.
471    /// An optional description for the slot, explaining its purpose.
472    identifier: FieldIdentifier,
473    /// Storage map entries, consisting of a list of keys associated with their values.
474    entries: Vec<MapEntry>,
475}
476
477impl MapRepresentation {
478    /// Creates a new `MapRepresentation` from a vector of map entries.
479    pub fn new(entries: Vec<MapEntry>, name: impl Into<StorageValueName>) -> Self {
480        Self {
481            entries,
482            identifier: FieldIdentifier::with_name(name.into()),
483        }
484    }
485
486    /// Sets the description of the [`MapRepresentation`] and returns `self`.
487    pub fn with_description(self, description: impl Into<String>) -> Self {
488        MapRepresentation {
489            entries: self.entries,
490            identifier: FieldIdentifier {
491                name: self.identifier.name,
492                description: Some(description.into()),
493            },
494        }
495    }
496
497    /// Returns an iterator over all of the storage entries' placeholder keys, alongside their
498    /// expected type.
499    pub fn template_requirements(&self) -> TemplateRequirementsIter<'_> {
500        Box::new(
501            self.entries
502                .iter()
503                .flat_map(move |entry| entry.template_requirements(self.identifier.name.clone())),
504        )
505    }
506
507    /// Returns a reference to map entries.
508    pub fn entries(&self) -> &[MapEntry] {
509        &self.entries
510    }
511
512    /// Returns a reference to the map's name within the storage metadata.
513    pub fn name(&self) -> &StorageValueName {
514        &self.identifier.name
515    }
516
517    /// Returns a reference to the field's description.
518    pub fn description(&self) -> Option<&String> {
519        self.identifier.description.as_ref()
520    }
521
522    /// Returns the number of key-value pairs in the map.
523    pub fn len(&self) -> usize {
524        self.entries.len()
525    }
526
527    /// Returns `true` if there are no entries in the map.
528    pub fn is_empty(&self) -> bool {
529        self.entries.is_empty()
530    }
531
532    /// Attempts to convert the [MapRepresentation] into a [StorageMap].
533    ///
534    /// If any of the inner elements are templates, their values are retrieved from
535    /// `init_storage_data`, identified by their key.
536    pub fn try_build_map(
537        &self,
538        init_storage_data: &InitStorageData,
539    ) -> Result<StorageMap, AccountComponentTemplateError> {
540        let entries = self
541            .entries
542            .iter()
543            .map(|map_entry| {
544                let key = map_entry
545                    .key()
546                    .try_build_word(init_storage_data, self.identifier.name.clone())?;
547                let value = map_entry
548                    .value()
549                    .try_build_word(init_storage_data, self.identifier.name.clone())?;
550                Ok((key, value))
551            })
552            .collect::<Result<Vec<(Word, Word)>, _>>()?;
553
554        StorageMap::with_entries(entries)
555            .map_err(|err| AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::new(err)))
556    }
557
558    /// Validates map keys by checking for duplicates.
559    ///
560    /// Because keys can be represented in a variety of ways, the `to_string()` implementation is
561    /// used to check for duplicates.  
562    pub(crate) fn validate(&self) -> Result<(), AccountComponentTemplateError> {
563        let mut seen_keys = BTreeSet::new();
564        for entry in self.entries() {
565            entry.key().validate()?;
566            entry.value().validate()?;
567            if let Ok(key) = entry
568                .key()
569                .try_build_word(&InitStorageData::default(), StorageValueName::empty())
570                && !seen_keys.insert(key)
571            {
572                return Err(AccountComponentTemplateError::StorageMapHasDuplicateKeys(Box::from(
573                    format!("key `{key}` is duplicated"),
574                )));
575            }
576        }
577
578        Ok(())
579    }
580}
581
582impl From<MapRepresentation> for Vec<MapEntry> {
583    fn from(value: MapRepresentation) -> Self {
584        value.entries
585    }
586}
587
588impl Serializable for MapRepresentation {
589    fn write_into<W: ByteWriter>(&self, target: &mut W) {
590        self.entries.write_into(target);
591        target.write(&self.identifier);
592    }
593}
594
595impl Deserializable for MapRepresentation {
596    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
597        let entries = Vec::<MapEntry>::read_from(source)?;
598        let identifier = FieldIdentifier::read_from(source)?;
599        Ok(Self { entries, identifier })
600    }
601}
602
603// MULTI-WORD VALUE
604// ================================================================================================
605
606/// Defines how multi-slot values are represented within the component's storage description.
607///
608/// Each multi-word value representation can be:
609/// - A predefined value that may contain a hardcoded word or a mix of fixed and templated felts.
610#[derive(Debug, Clone, PartialEq, Eq)]
611pub enum MultiWordRepresentation {
612    // TODO: Once there are multi-slot template types, add a MultiWordRepresentation::Template
613    // here
614    Value {
615        /// The human-readable name of this multi-slot entry.
616        identifier: FieldIdentifier,
617        /// A list of values to fill the logical slot, with a length equal to the number of slots.
618        values: Vec<[FeltRepresentation; 4]>,
619    },
620}
621
622impl MultiWordRepresentation {
623    /// Returns the number of words in this representation.
624    pub fn num_words(&self) -> usize {
625        match self {
626            MultiWordRepresentation::Value { values, .. } => values.len(),
627        }
628    }
629
630    /// Validates the multi-slot value.
631    pub fn validate(&self) -> Result<(), AccountComponentTemplateError> {
632        match self {
633            MultiWordRepresentation::Value { values, .. } => {
634                for slot_word in values {
635                    for felt_in_slot in slot_word {
636                        felt_in_slot.validate()?;
637                    }
638                }
639            },
640        }
641        Ok(())
642    }
643}
644
645impl Serializable for MultiWordRepresentation {
646    fn write_into<W: ByteWriter>(&self, target: &mut W) {
647        match self {
648            MultiWordRepresentation::Value { identifier, values } => {
649                target.write_u8(0u8);
650                target.write(identifier);
651                target.write(values);
652            },
653        }
654    }
655}
656impl Deserializable for MultiWordRepresentation {
657    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
658        let variant_tag = source.read_u8()?;
659        match variant_tag {
660            0 => {
661                let identifier: FieldIdentifier = source.read()?;
662                let values: Vec<[FeltRepresentation; 4]> = source.read()?;
663                Ok(MultiWordRepresentation::Value { identifier, values })
664            },
665            _ => Err(DeserializationError::InvalidValue(format!(
666                "unknown variant tag '{variant_tag}' for MultiWordRepresentation"
667            ))),
668        }
669    }
670}