miden_objects/account/component/template/storage/
placeholder.rs1use alloc::string::{String, ToString};
2
3use thiserror::Error;
4use vm_core::{
5 utils::{ByteReader, ByteWriter, Deserializable, Serializable},
6 Felt, Word,
7};
8use vm_processor::DeserializationError;
9
10use crate::account::{component::template::AccountComponentTemplateError, StorageMap};
11
12#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
24pub struct StoragePlaceholder {
25 key: String,
26}
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
32pub enum PlaceholderType {
33 Felt,
34 Map,
35 Word,
36}
37
38impl core::fmt::Display for PlaceholderType {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 match self {
41 PlaceholderType::Felt => f.write_str("Felt"),
42 PlaceholderType::Map => f.write_str("Map"),
43 PlaceholderType::Word => f.write_str("Word"),
44 }
45 }
46}
47
48impl StoragePlaceholder {
49 pub fn new(key: impl Into<String>) -> Result<Self, StoragePlaceholderError> {
64 let key: String = key.into();
65 Self::validate(&key)?;
66 Ok(Self { key })
67 }
68
69 pub fn inner(&self) -> &str {
71 &self.key
72 }
73
74 fn validate(key: &str) -> Result<(), StoragePlaceholderError> {
78 if key.is_empty() {
79 return Err(StoragePlaceholderError::EmptyKey);
80 }
81
82 for segment in key.split('.') {
83 if segment.is_empty() {
84 return Err(StoragePlaceholderError::EmptyKey);
85 }
86
87 for c in segment.chars() {
88 if !(c.is_ascii_alphanumeric() || c == '_' || c == '-') {
89 return Err(StoragePlaceholderError::InvalidChar(key.into(), c));
90 }
91 }
92 }
93
94 Ok(())
95 }
96}
97
98impl TryFrom<&str> for StoragePlaceholder {
99 type Error = StoragePlaceholderError;
100
101 fn try_from(value: &str) -> Result<Self, Self::Error> {
102 if value.starts_with("{{") && value.ends_with("}}") {
103 let inner = &value[2..value.len() - 2];
104 Self::validate(inner)?;
105
106 Ok(StoragePlaceholder { key: inner.to_string() })
107 } else {
108 Err(StoragePlaceholderError::FormatError(value.into()))
109 }
110 }
111}
112
113impl TryFrom<&String> for StoragePlaceholder {
114 type Error = StoragePlaceholderError;
115
116 fn try_from(value: &String) -> Result<Self, Self::Error> {
117 Self::try_from(value.as_str())
118 }
119}
120
121impl core::fmt::Display for StoragePlaceholder {
122 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123 write!(f, "{{{{{}}}}}", self.key)
124 }
125}
126
127#[derive(Debug, Error)]
128pub enum StoragePlaceholderError {
129 #[error("entire key and key segments cannot be empty")]
130 EmptyKey,
131 #[error("key `{0}` is invalid (expected string in {{...}} format)")]
132 FormatError(String),
133 #[error(
134 "key `{0}` contains invalid character ({1}) (must be alphanumeric, underscore, or hyphen)"
135 )]
136 InvalidChar(String, char),
137}
138
139impl Serializable for StoragePlaceholder {
143 fn write_into<W: ByteWriter>(&self, target: &mut W) {
144 target.write(&self.key);
145 }
146}
147
148impl Deserializable for StoragePlaceholder {
149 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
150 let key: String = source.read()?;
151 StoragePlaceholder::new(key)
152 .map_err(|err| DeserializationError::InvalidValue(err.to_string()))
153 }
154}
155
156#[derive(Clone, Debug)]
168pub enum StorageValue {
169 Felt(Felt),
170 Word(Word),
171 Map(StorageMap),
172}
173
174impl StorageValue {
175 pub fn as_felt(&self) -> Result<&Felt, AccountComponentTemplateError> {
177 if let StorageValue::Felt(felt) = self {
178 Ok(felt)
179 } else {
180 Err(AccountComponentTemplateError::IncorrectStorageValue("Felt".into()))
181 }
182 }
183
184 pub fn as_word(&self) -> Result<&Word, AccountComponentTemplateError> {
186 if let StorageValue::Word(word) = self {
187 Ok(word)
188 } else {
189 Err(AccountComponentTemplateError::IncorrectStorageValue("Word".into()))
190 }
191 }
192
193 pub fn as_map(&self) -> Result<&StorageMap, AccountComponentTemplateError> {
195 if let StorageValue::Map(map) = self {
196 Ok(map)
197 } else {
198 Err(AccountComponentTemplateError::IncorrectStorageValue("Map".into()))
199 }
200 }
201}