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

1use alloc::boxed::Box;
2use alloc::collections::BTreeMap;
3use alloc::string::{String, ToString};
4use core::error::Error;
5use core::fmt::{self, Display};
6
7use miden_core::utils::{ByteReader, ByteWriter, Deserializable, Serializable};
8use miden_core::{Felt, Word};
9use miden_crypto::dsa::rpo_falcon512::{self};
10use miden_crypto::word::parse_hex_string_as_word;
11use miden_processor::DeserializationError;
12use thiserror::Error;
13
14use crate::asset::TokenSymbol;
15use crate::utils::sync::LazyLock;
16
17/// A global registry for template converters.
18///
19/// It is used during component instantiation to dynamically convert template placeholders into
20/// their respective storage values.
21pub static TEMPLATE_REGISTRY: LazyLock<TemplateRegistry> = LazyLock::new(|| {
22    let mut registry = TemplateRegistry::new();
23    registry.register_felt_type::<u8>();
24    registry.register_felt_type::<u16>();
25    registry.register_felt_type::<u32>();
26    registry.register_felt_type::<Felt>();
27    registry.register_felt_type::<TokenSymbol>();
28    registry.register_word_type::<Word>();
29    registry.register_word_type::<rpo_falcon512::PublicKey>();
30    registry
31});
32
33// STORAGE VALUE NAME
34// ================================================================================================
35
36/// A simple wrapper type around a string key that identifies values.
37///
38/// A storage value name is a string that identifies dynamic values within a component's metadata
39/// storage entries.
40///
41/// These names can be chained together, in a way that allows composing unique keys for inner
42/// templated elements.
43///
44/// At component instantiation, a map of names to values must be provided to dynamically
45/// replace these placeholders with the instance’s actual values.
46#[derive(Clone, Debug, Ord, PartialOrd, PartialEq, Eq)]
47#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
48#[cfg_attr(feature = "std", serde(transparent))]
49pub struct StorageValueName {
50    fully_qualified_name: String,
51}
52
53impl StorageValueName {
54    /// Creates a new [`StorageValueName`] from the provided string.
55    ///
56    /// A [`StorageValueName`] serves as an identifier for storage values that are determined at
57    /// instantiation time of an [AccountComponentTemplate](super::super::AccountComponentTemplate).
58    ///
59    /// The key can consist of one or more segments separated by dots (`.`).  
60    /// Each segment must be non-empty and may contain only alphanumeric characters, underscores
61    /// (`_`), or hyphens (`-`).
62    ///
63    /// # Errors
64    ///
65    /// This method returns an error if:
66    /// - Any segment (or the whole key) is empty.
67    /// - Any segment contains invalid characters.
68    pub fn new(base: impl Into<String>) -> Result<Self, StorageValueNameError> {
69        let base: String = base.into();
70        for segment in base.split('.') {
71            Self::validate_segment(segment)?;
72        }
73        Ok(Self { fully_qualified_name: base })
74    }
75
76    /// Creates an empty [`StorageValueName`].
77    pub(crate) fn empty() -> Self {
78        StorageValueName { fully_qualified_name: String::default() }
79    }
80
81    /// Adds a suffix to the storage value name, separated by a period.
82    #[must_use]
83    pub fn with_suffix(self, suffix: &StorageValueName) -> StorageValueName {
84        let mut key = self;
85        if !suffix.as_str().is_empty() {
86            if !key.as_str().is_empty() {
87                key.fully_qualified_name.push('.');
88            }
89            key.fully_qualified_name.push_str(suffix.as_str());
90        }
91
92        key
93    }
94
95    /// Returns the fully qualified name as a string slice.
96    pub fn as_str(&self) -> &str {
97        &self.fully_qualified_name
98    }
99
100    fn validate_segment(segment: &str) -> Result<(), StorageValueNameError> {
101        if segment.is_empty() {
102            return Err(StorageValueNameError::EmptySegment);
103        }
104        if let Some(offending_char) =
105            segment.chars().find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == '-'))
106        {
107            return Err(StorageValueNameError::InvalidCharacter {
108                part: segment.to_string(),
109                character: offending_char,
110            });
111        }
112
113        Ok(())
114    }
115}
116
117impl Display for StorageValueName {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        f.write_str(self.as_str())
120    }
121}
122
123impl Serializable for StorageValueName {
124    fn write_into<W: ByteWriter>(&self, target: &mut W) {
125        target.write(&self.fully_qualified_name);
126    }
127}
128
129impl Deserializable for StorageValueName {
130    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
131        let key: String = source.read()?;
132        Ok(StorageValueName { fully_qualified_name: key })
133    }
134}
135
136#[derive(Debug, Error)]
137pub enum StorageValueNameError {
138    #[error("key segment is empty")]
139    EmptySegment,
140    #[error("key segment '{part}' contains invalid character '{character}'")]
141    InvalidCharacter { part: String, character: char },
142}
143
144// TEMPLATE TYPE ERROR
145// ================================================================================================
146
147/// Errors that can occur when parsing or converting template types.
148///
149/// This enum covers various failure cases including parsing errors, conversion errors,
150/// unsupported conversions, and cases where a required type is not found in the registry.
151#[derive(Debug, Error)]
152pub enum TemplateTypeError {
153    #[error("conversion error: {0}")]
154    ConversionError(String),
155    #[error("felt type ` {0}` not found in the type registry")]
156    FeltTypeNotFound(TemplateType),
157    #[error("invalid type name `{0}`: {1}")]
158    InvalidTypeName(String, String),
159    #[error("failed to parse input `{input}` as `{template_type}`")]
160    ParseError {
161        input: String,
162        template_type: TemplateType,
163        source: Box<dyn Error + Send + Sync + 'static>,
164    },
165    #[error("word type ` {0}` not found in the type registry")]
166    WordTypeNotFound(TemplateType),
167}
168
169impl TemplateTypeError {
170    /// Creates a [`TemplateTypeError::ParseError`].
171    pub fn parse(
172        input: impl Into<String>,
173        template_type: TemplateType,
174        source: impl Error + Send + Sync + 'static,
175    ) -> Self {
176        TemplateTypeError::ParseError {
177            input: input.into(),
178            template_type,
179            source: Box::new(source),
180        }
181    }
182}
183
184// TEMPLATE TYPE
185// ================================================================================================
186
187/// A newtype wrapper around a `String`, representing a template's type identifier.
188#[derive(Debug, Clone, PartialEq, Eq, Ord, PartialOrd)]
189#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
190#[cfg_attr(feature = "std", serde(transparent))]
191pub struct TemplateType(String);
192
193impl TemplateType {
194    /// Creates a new [`TemplateType`] from a `String`.
195    ///
196    /// The name must follow a Rust-style namespace format, consisting of one or more segments
197    /// (non-empty, and alphanumerical) separated by double-colon (`::`) delimiters.
198    ///
199    /// # Errors
200    ///
201    /// - If the identifier is empty.
202    /// - If it is composed of one or more segments separated by `::`.
203    /// - If any segment is empty or contains something other than alphanumerical
204    ///   characters/underscores.
205    pub fn new(s: impl Into<String>) -> Result<Self, TemplateTypeError> {
206        let s = s.into();
207        if s.is_empty() {
208            return Err(TemplateTypeError::InvalidTypeName(
209                s.clone(),
210                "template type identifier is empty".to_string(),
211            ));
212        }
213        for segment in s.split("::") {
214            if segment.is_empty() {
215                return Err(TemplateTypeError::InvalidTypeName(
216                    s.clone(),
217                    "empty segment in template type identifier".to_string(),
218                ));
219            }
220            if !segment.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
221                return Err(TemplateTypeError::InvalidTypeName(
222                    s.clone(),
223                    format!("segment '{segment}' contains invalid characters"),
224                ));
225            }
226        }
227        Ok(Self(s))
228    }
229
230    /// Returns the [`TemplateType`] for the native [`Felt`] type.
231    pub fn native_felt() -> TemplateType {
232        TemplateType::new("felt").expect("type is well formed")
233    }
234
235    /// Returns the [`TemplateType`] for the native [`Word`] type.
236    pub fn native_word() -> TemplateType {
237        TemplateType::new("word").expect("type is well formed")
238    }
239
240    /// Returns a reference to the inner string.
241    pub fn as_str(&self) -> &str {
242        &self.0
243    }
244}
245
246impl Display for TemplateType {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        f.write_str(self.as_str())
249    }
250}
251
252impl Serializable for TemplateType {
253    fn write_into<W: ByteWriter>(&self, target: &mut W) {
254        target.write(self.0.clone())
255    }
256}
257
258impl Deserializable for TemplateType {
259    fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
260        let id: String = source.read()?;
261
262        TemplateType::new(id).map_err(|err| DeserializationError::InvalidValue(err.to_string()))
263    }
264}
265
266// TEMPLATE REQUIREMENT
267// ================================================================================================
268
269/// Describes the expected type and additional metadata for a templated storage entry.
270///
271/// A `PlaceholderTypeRequirement` specifies the expected type identifier for a storage entry as
272/// well as an optional description. This information is used to validate and provide context for
273/// dynamic storage values.
274#[derive(Debug)]
275pub struct PlaceholderTypeRequirement {
276    /// The expected type identifier.
277    pub r#type: TemplateType,
278    /// An optional description providing additional context.
279    pub description: Option<String>,
280}
281
282// TEMPLATE TRAITS
283// ================================================================================================
284
285/// Trait for converting a string into a single `Felt`.
286pub trait TemplateFelt {
287    /// Returns the type identifier.
288    fn type_name() -> TemplateType;
289    /// Parses the input string into a `Felt`.
290    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError>;
291}
292
293/// Trait for converting a string into a single `Word`.
294pub trait TemplateWord {
295    /// Returns the type identifier.
296    fn type_name() -> TemplateType;
297    /// Parses the input string into a `Word`.
298    fn parse_word(input: &str) -> Result<Word, TemplateTypeError>;
299}
300
301// FELT IMPLS FOR NATIVE TYPES
302// ================================================================================================
303
304impl TemplateFelt for u8 {
305    fn type_name() -> TemplateType {
306        TemplateType::new("u8").expect("type is well formed")
307    }
308
309    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
310        let native: u8 = input
311            .parse()
312            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
313        Ok(Felt::from(native))
314    }
315}
316
317impl TemplateFelt for u16 {
318    fn type_name() -> TemplateType {
319        TemplateType::new("u16").expect("type is well formed")
320    }
321
322    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
323        let native: u16 = input
324            .parse()
325            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
326        Ok(Felt::from(native))
327    }
328}
329
330impl TemplateFelt for u32 {
331    fn type_name() -> TemplateType {
332        TemplateType::new("u32").expect("type is well formed")
333    }
334
335    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
336        let native: u32 = input
337            .parse()
338            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
339        Ok(Felt::from(native))
340    }
341}
342
343impl TemplateFelt for Felt {
344    fn type_name() -> TemplateType {
345        TemplateType::new("felt").expect("type is well formed")
346    }
347
348    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
349        let n = if let Some(hex) = input.strip_prefix("0x").or_else(|| input.strip_prefix("0X")) {
350            u64::from_str_radix(hex, 16)
351        } else {
352            input.parse::<u64>()
353        }
354        .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
355        Felt::try_from(n).map_err(|_| TemplateTypeError::ConversionError(input.to_string()))
356    }
357}
358
359impl TemplateFelt for TokenSymbol {
360    fn type_name() -> TemplateType {
361        TemplateType::new("token_symbol").expect("type is well formed")
362    }
363    fn parse_felt(input: &str) -> Result<Felt, TemplateTypeError> {
364        let token = TokenSymbol::new(input)
365            .map_err(|err| TemplateTypeError::parse(input.to_string(), Self::type_name(), err))?;
366        Ok(Felt::from(token))
367    }
368}
369
370// WORD IMPLS FOR NATIVE TYPES
371// ================================================================================================
372
373#[derive(Debug, Error)]
374#[error("error parsing word: {0}")]
375struct WordParseError(String);
376
377impl TemplateWord for Word {
378    fn type_name() -> TemplateType {
379        TemplateType::native_word()
380    }
381    fn parse_word(input: &str) -> Result<Word, TemplateTypeError> {
382        parse_hex_string_as_word(input)
383            .map_err(|err| {
384                TemplateTypeError::parse(
385                    Self::type_name().as_str(),
386                    Self::type_name(),
387                    WordParseError(err.into()),
388                )
389            })
390            .map(Word::from)
391    }
392}
393
394impl TemplateWord for rpo_falcon512::PublicKey {
395    fn type_name() -> TemplateType {
396        TemplateType::new("auth::rpo_falcon512::pub_key").expect("type is well formed")
397    }
398    fn parse_word(input: &str) -> Result<Word, TemplateTypeError> {
399        parse_hex_string_as_word(input)
400            .map_err(|err| {
401                TemplateTypeError::parse(
402                    input.to_string(),
403                    Self::type_name(),
404                    WordParseError(err.into()),
405                )
406            })
407            .map(Word::from)
408    }
409}
410
411// TYPE ALIASES FOR CONVERTER CLOSURES
412// ================================================================================================
413
414/// Type alias for a function that converts a string into a [`Felt`] value.
415type TemplateFeltConverter = fn(&str) -> Result<Felt, TemplateTypeError>;
416
417/// Type alias for a function that converts a string into a [`Word`].
418type TemplateWordConverter = fn(&str) -> Result<Word, TemplateTypeError>;
419
420// TODO: Implement converting to list of words for multi-slot values
421
422// TEMPLATE REGISTRY
423// ================================================================================================
424
425/// Registry for template converters.
426///
427/// This registry maintains mappings from type identifiers (as strings) to conversion functions for
428/// [`Felt`], [`Word`], and [`Vec<Word>`] types. It is used to dynamically parse template inputs
429/// into their corresponding storage representations.
430#[derive(Clone, Debug, Default)]
431pub struct TemplateRegistry {
432    felt: BTreeMap<TemplateType, TemplateFeltConverter>,
433    word: BTreeMap<TemplateType, TemplateWordConverter>,
434}
435
436impl TemplateRegistry {
437    /// Creates a new, empty `TemplateRegistry`.
438    ///
439    /// The registry is initially empty and conversion functions can be registered using the
440    /// `register_*_type` methods.
441    pub fn new() -> Self {
442        Self { ..Default::default() }
443    }
444
445    /// Registers a `TemplateFelt` converter, to interpret a string as a [`Felt``].
446    pub fn register_felt_type<T: TemplateFelt + 'static>(&mut self) {
447        let key = T::type_name();
448        self.felt.insert(key, T::parse_felt);
449    }
450
451    /// Registers a `TemplateWord` converter, to interpret a string as a [`Word`].
452    pub fn register_word_type<T: TemplateWord + 'static>(&mut self) {
453        let key = T::type_name();
454        self.word.insert(key, T::parse_word);
455    }
456
457    /// Attempts to parse a string into a `Felt` using the registered converter for the given type
458    /// name.
459    ///
460    /// # Arguments
461    ///
462    /// - type_name: A string that acts as the type identifier.
463    /// - value: The string representation of the value to be parsed.
464    ///
465    /// # Errors
466    ///
467    /// - If the type is not registered or if the conversion fails.
468    pub fn try_parse_felt(
469        &self,
470        type_name: &TemplateType,
471        value: &str,
472    ) -> Result<Felt, TemplateTypeError> {
473        let converter = self
474            .felt
475            .get(type_name)
476            .ok_or(TemplateTypeError::FeltTypeNotFound(type_name.clone()))?;
477        converter(value)
478    }
479
480    /// Attempts to parse a string into a `Word` using the registered converter for the given type
481    /// name.
482    ///
483    /// # Arguments
484    ///
485    /// - type_name: A string that acts as the type identifier.
486    /// - value: The string representation of the value to be parsed.
487    ///
488    /// # Errors
489    ///
490    /// - If the type is not registered or if the conversion fails.
491    pub fn try_parse_word(
492        &self,
493        type_name: &TemplateType,
494        value: &str,
495    ) -> Result<Word, TemplateTypeError> {
496        let converter = self
497            .word
498            .get(type_name)
499            .ok_or(TemplateTypeError::WordTypeNotFound(type_name.clone()))?;
500        converter(value)
501    }
502
503    /// Returns `true` if a `TemplateFelt` is registered for the given type.
504    pub fn contains_felt_type(&self, type_name: &TemplateType) -> bool {
505        self.felt.contains_key(type_name)
506    }
507
508    /// Returns `true` if a `TemplateWord` is registered for the given type.
509    pub fn contains_word_type(&self, type_name: &TemplateType) -> bool {
510        self.word.contains_key(type_name)
511    }
512}