miden_protocol/account/component/storage/schema/
word.rs1use alloc::collections::BTreeMap;
2use alloc::string::{String, ToString};
3
4use super::super::type_registry::{SCHEMA_TYPE_REGISTRY, SchemaRequirement, SchemaType};
5use super::super::{InitStorageData, StorageValueName};
6use super::FeltSchema;
7use crate::account::StorageSlotName;
8use crate::errors::ComponentMetadataError;
9use crate::utils::serde::{
10 ByteReader,
11 ByteWriter,
12 Deserializable,
13 DeserializationError,
14 Serializable,
15};
16use crate::{Felt, Word};
17
18#[derive(Debug, Clone, PartialEq, Eq)]
26#[allow(clippy::large_enum_variant)]
27pub enum WordSchema {
28 Simple {
30 r#type: SchemaType,
31 default_value: Option<Word>,
32 },
33 Composite { value: [FeltSchema; 4] },
35}
36
37impl WordSchema {
38 pub fn new_simple(r#type: SchemaType) -> Self {
39 WordSchema::Simple { r#type, default_value: None }
40 }
41
42 pub fn new_simple_with_default(r#type: SchemaType, default_value: Word) -> Self {
43 WordSchema::Simple {
44 r#type,
45 default_value: Some(default_value),
46 }
47 }
48
49 pub fn new_value(value: impl Into<[FeltSchema; 4]>) -> Self {
50 WordSchema::Composite { value: value.into() }
51 }
52
53 pub fn value(&self) -> Option<&[FeltSchema; 4]> {
54 match self {
55 WordSchema::Composite { value } => Some(value),
56 WordSchema::Simple { .. } => None,
57 }
58 }
59
60 pub fn word_type(&self) -> SchemaType {
62 match self {
63 WordSchema::Simple { r#type, .. } => r#type.clone(),
64 WordSchema::Composite { .. } => SchemaType::native_word(),
65 }
66 }
67
68 pub(super) fn collect_init_value_requirements(
69 &self,
70 value_name: StorageValueName,
71 description: Option<String>,
72 requirements: &mut BTreeMap<StorageValueName, SchemaRequirement>,
73 ) -> Result<(), ComponentMetadataError> {
74 match self {
75 WordSchema::Simple { r#type, default_value } => {
76 if *r#type == SchemaType::void() {
77 return Ok(());
78 }
79
80 let default_value = default_value.map(|word| {
81 SCHEMA_TYPE_REGISTRY.display_word(r#type, word).value().to_string()
82 });
83
84 if requirements
85 .insert(
86 value_name.clone(),
87 SchemaRequirement {
88 description,
89 r#type: r#type.clone(),
90 default_value,
91 },
92 )
93 .is_some()
94 {
95 return Err(ComponentMetadataError::DuplicateInitValueName(value_name));
96 }
97
98 Ok(())
99 },
100 WordSchema::Composite { value } => {
101 for felt in value.iter() {
102 felt.collect_init_value_requirements(value_name.clone(), requirements)?;
103 }
104 Ok(())
105 },
106 }
107 }
108
109 pub(super) fn validate(&self) -> Result<(), ComponentMetadataError> {
111 let type_exists = SCHEMA_TYPE_REGISTRY.contains_word_type(&self.word_type());
112 if !type_exists {
113 return Err(ComponentMetadataError::InvalidType(
114 self.word_type().to_string(),
115 "Word".into(),
116 ));
117 }
118
119 if let WordSchema::Simple {
120 r#type,
121 default_value: Some(default_value),
122 } = self
123 {
124 SCHEMA_TYPE_REGISTRY
125 .validate_word_value(r#type, *default_value)
126 .map_err(ComponentMetadataError::StorageValueParsingError)?;
127 }
128
129 if let Some(felts) = self.value() {
130 for felt in felts {
131 felt.validate()?;
132 }
133 }
134
135 Ok(())
136 }
137
138 pub(crate) fn try_build_word(
144 &self,
145 init_storage_data: &InitStorageData,
146 slot_name: &StorageSlotName,
147 ) -> Result<Word, ComponentMetadataError> {
148 let slot_prefix = StorageValueName::from_slot_name(slot_name);
149 let slot_value = init_storage_data.slot_value_entry(slot_name);
150 let has_fields = init_storage_data.has_field_entries_for_slot(slot_name);
151
152 if init_storage_data.map_entries(slot_name).is_some() {
153 return Err(ComponentMetadataError::InvalidInitStorageValue(
154 slot_prefix,
155 "expected a value, got a map".into(),
156 ));
157 }
158
159 match self {
160 WordSchema::Simple { r#type, default_value } => {
161 if has_fields {
162 return Err(ComponentMetadataError::InvalidInitStorageValue(
163 slot_prefix,
164 "expected a value, got field entries".into(),
165 ));
166 }
167 match slot_value {
168 Some(value) => {
169 super::parse_storage_value_with_schema(self, value, &slot_prefix)
170 },
171 None => {
172 if *r#type == SchemaType::void() {
173 Ok(Word::empty())
174 } else {
175 default_value.as_ref().copied().ok_or_else(|| {
176 ComponentMetadataError::InitValueNotProvided(slot_prefix)
177 })
178 }
179 },
180 }
181 },
182 WordSchema::Composite { value } => {
183 if let Some(value) = slot_value {
184 if has_fields {
185 return Err(ComponentMetadataError::InvalidInitStorageValue(
186 slot_prefix,
187 "expected a single value, got both value and field entries".into(),
188 ));
189 }
190 return super::parse_storage_value_with_schema(self, value, &slot_prefix);
191 }
192
193 let mut result = [Felt::ZERO; 4];
194 for (index, felt_schema) in value.iter().enumerate() {
195 result[index] = felt_schema.try_build_felt(init_storage_data, slot_name)?;
196 }
197 Ok(Word::from(result))
198 },
199 }
200 }
201
202 pub(crate) fn validate_word_value(
203 &self,
204 slot_prefix: &StorageValueName,
205 label: &str,
206 word: Word,
207 ) -> Result<(), ComponentMetadataError> {
208 match self {
209 WordSchema::Simple { r#type, .. } => {
210 SCHEMA_TYPE_REGISTRY.validate_word_value(r#type, word).map_err(|err| {
211 ComponentMetadataError::InvalidInitStorageValue(
212 slot_prefix.clone(),
213 format!("{label} does not match `{}`: {err}", r#type),
214 )
215 })
216 },
217 WordSchema::Composite { value } => {
218 for (index, felt_schema) in value.iter().enumerate() {
219 let felt_type = felt_schema.felt_type();
220 SCHEMA_TYPE_REGISTRY.validate_felt_value(&felt_type, word[index]).map_err(
221 |err| {
222 ComponentMetadataError::InvalidInitStorageValue(
223 slot_prefix.clone(),
224 format!("{label}[{index}] does not match `{felt_type}`: {err}"),
225 )
226 },
227 )?;
228 }
229
230 Ok(())
231 },
232 }
233 }
234
235 pub(super) fn write_into_with_optional_defaults<W: ByteWriter>(
236 &self,
237 target: &mut W,
238 include_defaults: bool,
239 ) {
240 match self {
241 WordSchema::Simple { r#type, default_value } => {
242 target.write_u8(0);
243 target.write(r#type);
244 let default_value = if include_defaults { *default_value } else { None };
245 target.write(default_value);
246 },
247 WordSchema::Composite { value } => {
248 target.write_u8(1);
249 for felt in value.iter() {
250 felt.write_into_with_optional_defaults(target, include_defaults);
251 }
252 },
253 }
254 }
255}
256
257impl Serializable for WordSchema {
258 fn write_into<W: ByteWriter>(&self, target: &mut W) {
259 self.write_into_with_optional_defaults(target, true);
260 }
261}
262
263impl Deserializable for WordSchema {
264 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
265 let tag = source.read_u8()?;
266 match tag {
267 0 => {
268 let r#type = SchemaType::read_from(source)?;
269 let default_value = Option::<Word>::read_from(source)?;
270 Ok(WordSchema::Simple { r#type, default_value })
271 },
272 1 => {
273 let value = <[FeltSchema; 4]>::read_from(source)?;
274 Ok(WordSchema::Composite { value })
275 },
276 other => Err(DeserializationError::InvalidValue(format!(
277 "unknown tag '{other}' for WordSchema"
278 ))),
279 }
280 }
281}
282
283impl From<SchemaType> for WordSchema {
284 fn from(r#type: SchemaType) -> Self {
285 WordSchema::new_simple(r#type)
286 }
287}
288
289impl From<[FeltSchema; 4]> for WordSchema {
290 fn from(value: [FeltSchema; 4]) -> Self {
291 WordSchema::new_value(value)
292 }
293}
294
295impl From<[Felt; 4]> for WordSchema {
296 fn from(value: [Felt; 4]) -> Self {
297 WordSchema::new_simple_with_default(SchemaType::native_word(), Word::from(value))
298 }
299}