Skip to main content

miden_protocol/account/component/storage/toml/
init_storage_data.rs

1use alloc::string::{String, ToString};
2
3use serde::Deserialize;
4use thiserror::Error;
5
6use super::super::{
7    InitStorageData,
8    InitStorageDataError as CoreInitStorageDataError,
9    StorageValueName,
10    StorageValueNameError,
11    WordValue,
12};
13use super::RawMapEntrySchema;
14
15impl InitStorageData {
16    /// Creates an instance of [`InitStorageData`] from a TOML string.
17    ///
18    /// # Supported formats
19    ///
20    /// ```toml
21    /// # Value entry (string)
22    /// "slot::name" = "0x1234"
23    ///
24    /// # Value entry (4-element word)
25    /// "slot::name" = ["0", "0", "0", "100"]
26    ///
27    /// # Nested table (flattened to slot::name.field)
28    /// ["slot::name"]
29    /// field = "value"
30    ///
31    /// # Map entries
32    /// "slot::map" = [
33    ///     { key = "0x01", value = "0x10" },
34    /// ]
35    /// ```
36    pub fn from_toml(toml_str: &str) -> Result<Self, InitStorageDataError> {
37        let table: toml::Table = toml::from_str(toml_str)?;
38        let mut data = InitStorageData::default();
39
40        for (key, value) in table {
41            let name: StorageValueName =
42                key.parse().map_err(InitStorageDataError::InvalidStorageValueName)?;
43
44            match value {
45                // ["slot::name"]
46                // field = "value"
47                toml::Value::Table(nested) => {
48                    if nested.is_empty() {
49                        return Err(InitStorageDataError::EmptyTable(name.to_string()));
50                    }
51                    if name.field_name().is_some() {
52                        return Err(InitStorageDataError::ExcessiveNesting(name.to_string()));
53                    }
54                    for (field, field_value) in nested {
55                        let field_name =
56                            StorageValueName::from_slot_name_with_suffix(name.slot_name(), &field)
57                                .map_err(InitStorageDataError::InvalidStorageValueName)?;
58                        let word = WordValue::deserialize(field_value).map_err(|_| {
59                            InitStorageDataError::InvalidValue(field_name.to_string())
60                        })?;
61                        data.insert_value(field_name, word)?;
62                    }
63                },
64                // "slot::name" = [{ key = "...", value = "..." }, ...]
65                toml::Value::Array(items)
66                    if items.iter().all(|v| matches!(v, toml::Value::Table(_))) =>
67                {
68                    if name.field_name().is_some() {
69                        return Err(InitStorageDataError::InvalidMapEntryKey(name.to_string()));
70                    }
71                    for item in items {
72                        // Try deserializing as map entry
73                        let entry: RawMapEntrySchema = RawMapEntrySchema::deserialize(item)
74                            .map_err(|e| {
75                                InitStorageDataError::InvalidMapEntrySchema(e.to_string())
76                            })?;
77
78                        data.insert_map_entry(name.slot_name().clone(), entry.key, entry.value)?;
79                    }
80                },
81                // "slot::name" = "value" or "slot::name" = ["a", "b", "c", "d"]
82                other => {
83                    let word = WordValue::deserialize(other)
84                        .map_err(|_| InitStorageDataError::InvalidValue(name.to_string()))?;
85                    data.insert_value(name, word)?;
86                },
87            }
88        }
89
90        Ok(data)
91    }
92}
93
94#[derive(Debug, Error)]
95pub enum InitStorageDataError {
96    #[error("failed to parse TOML: {0}")]
97    InvalidToml(#[from] toml::de::Error),
98
99    #[error("empty table encountered for key `{0}`")]
100    EmptyTable(String),
101
102    #[error(transparent)]
103    InvalidData(#[from] CoreInitStorageDataError),
104
105    #[error("invalid map entry key `{0}`: map entries must target a slot name")]
106    InvalidMapEntryKey(String),
107
108    #[error("excessive nesting for key `{0}`: only one level of table nesting is allowed")]
109    ExcessiveNesting(String),
110
111    #[error(
112        "invalid input for `{0}`: expected a string, a 4-element string array, or a map entry list"
113    )]
114    InvalidValue(String),
115
116    #[error("invalid storage value name")]
117    InvalidStorageValueName(#[source] StorageValueNameError),
118
119    #[error("invalid map entry: {0}")]
120    InvalidMapEntrySchema(String),
121}