miden_protocol/account/component/storage/
value_name.rs1use alloc::string::{String, ToString};
2use core::cmp::Ordering;
3use core::fmt::{self, Display};
4use core::str::FromStr;
5
6use thiserror::Error;
7
8use crate::account::StorageSlotName;
9use crate::errors::StorageSlotNameError;
10use crate::utils::serde::{
11 ByteReader,
12 ByteWriter,
13 Deserializable,
14 DeserializationError,
15 Serializable,
16};
17
18#[derive(Clone, Debug)]
26#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
27#[cfg_attr(feature = "std", serde(try_from = "String", into = "String"))]
28pub struct StorageValueName {
29 slot_name: StorageSlotName,
30 element_field: Option<String>,
31}
32
33impl StorageValueName {
34 pub fn from_slot_name(slot_name: &StorageSlotName) -> Self {
36 StorageValueName {
37 slot_name: slot_name.clone(),
38 element_field: None,
39 }
40 }
41
42 pub fn from_slot_name_with_suffix(
48 slot_name: &StorageSlotName,
49 suffix: &str,
50 ) -> Result<StorageValueName, StorageValueNameError> {
51 Self::validate_field_segment(suffix)?;
52 Ok(StorageValueName {
53 slot_name: slot_name.clone(),
54 element_field: Some(suffix.to_string()),
55 })
56 }
57
58 pub fn slot_name(&self) -> &StorageSlotName {
60 &self.slot_name
61 }
62
63 pub fn field_name(&self) -> Option<&str> {
65 self.element_field.as_deref()
66 }
67
68 fn validate_field_segment(segment: &str) -> Result<(), StorageValueNameError> {
69 if segment.is_empty() {
70 return Err(StorageValueNameError::EmptySuffix);
71 }
72
73 if let Some(offending_char) =
74 segment.chars().find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == '-'))
75 {
76 return Err(StorageValueNameError::InvalidCharacter {
77 part: segment.to_string(),
78 character: offending_char,
79 });
80 }
81
82 Ok(())
83 }
84}
85
86impl PartialEq for StorageValueName {
87 fn eq(&self, other: &Self) -> bool {
88 self.slot_name.as_str() == other.slot_name.as_str()
89 && self.element_field.as_deref() == other.element_field.as_deref()
90 }
91}
92
93impl Eq for StorageValueName {}
94
95impl PartialOrd for StorageValueName {
96 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
97 Some(self.cmp(other))
98 }
99}
100
101impl Ord for StorageValueName {
102 fn cmp(&self, other: &Self) -> Ordering {
103 let slot_cmp = self.slot_name.as_str().cmp(other.slot_name.as_str());
104 if slot_cmp != Ordering::Equal {
105 return slot_cmp;
106 }
107
108 match (self.element_field.as_deref(), other.element_field.as_deref()) {
109 (None, None) => Ordering::Equal,
110
111 (None, Some(_)) => Ordering::Less,
113 (Some(_), None) => Ordering::Greater,
114
115 (Some(a), Some(b)) => a.cmp(b),
116 }
117 }
118}
119
120impl FromStr for StorageValueName {
121 type Err = StorageValueNameError;
122
123 fn from_str(value: &str) -> Result<Self, Self::Err> {
124 if value.is_empty() {
125 return Err(StorageValueNameError::EmptySuffix);
126 }
127
128 let (slot, field) = match value.split_once('.') {
132 Some((slot, field)) => {
133 Self::validate_field_segment(field)?;
134
135 if slot.is_empty() || field.is_empty() {
136 return Err(StorageValueNameError::EmptySuffix);
137 }
138
139 (slot, Some(field))
140 },
141 None => (value, None),
142 };
143
144 let slot_name =
145 StorageSlotName::new(slot).map_err(StorageValueNameError::InvalidSlotName)?;
146 let field = match field {
147 Some(field) => {
148 Self::validate_field_segment(field)?;
149 Some(field.to_string())
150 },
151 None => None,
152 };
153
154 Ok(Self { slot_name, element_field: field })
155 }
156}
157
158impl TryFrom<String> for StorageValueName {
159 type Error = StorageValueNameError;
160
161 fn try_from(value: String) -> Result<Self, Self::Error> {
162 value.parse()
163 }
164}
165
166impl TryFrom<&str> for StorageValueName {
167 type Error = StorageValueNameError;
168
169 fn try_from(value: &str) -> Result<Self, Self::Error> {
170 value.parse()
171 }
172}
173
174impl From<StorageValueName> for String {
175 fn from(value: StorageValueName) -> Self {
176 value.to_string()
177 }
178}
179
180impl From<&StorageSlotName> for StorageValueName {
181 fn from(value: &StorageSlotName) -> Self {
182 StorageValueName::from_slot_name(value)
183 }
184}
185
186impl Display for StorageValueName {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 match &self.element_field {
189 None => f.write_str(self.slot_name.as_str()),
190 Some(field) => {
191 f.write_str(self.slot_name.as_str())?;
192 f.write_str(".")?;
193 f.write_str(field)
194 },
195 }
196 }
197}
198
199impl Serializable for StorageValueName {
200 fn write_into<W: ByteWriter>(&self, target: &mut W) {
201 let key = self.to_string();
202 target.write(&key);
203 }
204}
205
206impl Deserializable for StorageValueName {
207 fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
208 let key: String = source.read()?;
209 key.parse().map_err(|err: StorageValueNameError| {
210 DeserializationError::InvalidValue(err.to_string())
211 })
212 }
213}
214
215#[derive(Debug, Error)]
216pub enum StorageValueNameError {
217 #[error("key suffix is empty")]
218 EmptySuffix,
219 #[error("key segment '{part}' contains invalid character '{character}'")]
220 InvalidCharacter { part: String, character: char },
221 #[error("invalid storage slot name")]
222 InvalidSlotName(#[source] StorageSlotNameError),
223}