use alloc::string::{String, ToString};
use core::cmp::Ordering;
use core::fmt::{self, Display};
use core::str::FromStr;
use thiserror::Error;
use crate::account::StorageSlotName;
use crate::errors::StorageSlotNameError;
use crate::utils::serde::{
ByteReader,
ByteWriter,
Deserializable,
DeserializationError,
Serializable,
};
#[derive(Clone, Debug)]
#[cfg_attr(feature = "std", derive(::serde::Deserialize, ::serde::Serialize))]
#[cfg_attr(feature = "std", serde(try_from = "String", into = "String"))]
pub struct StorageValueName {
slot_name: StorageSlotName,
element_field: Option<String>,
}
impl StorageValueName {
pub fn from_slot_name(slot_name: &StorageSlotName) -> Self {
StorageValueName {
slot_name: slot_name.clone(),
element_field: None,
}
}
pub fn from_slot_name_with_suffix(
slot_name: &StorageSlotName,
suffix: &str,
) -> Result<StorageValueName, StorageValueNameError> {
Self::validate_field_segment(suffix)?;
Ok(StorageValueName {
slot_name: slot_name.clone(),
element_field: Some(suffix.to_string()),
})
}
pub fn slot_name(&self) -> &StorageSlotName {
&self.slot_name
}
pub fn field_name(&self) -> Option<&str> {
self.element_field.as_deref()
}
fn validate_field_segment(segment: &str) -> Result<(), StorageValueNameError> {
if segment.is_empty() {
return Err(StorageValueNameError::EmptySuffix);
}
if let Some(offending_char) =
segment.chars().find(|&c| !(c.is_ascii_alphanumeric() || c == '_' || c == '-'))
{
return Err(StorageValueNameError::InvalidCharacter {
part: segment.to_string(),
character: offending_char,
});
}
Ok(())
}
}
impl PartialEq for StorageValueName {
fn eq(&self, other: &Self) -> bool {
self.slot_name.as_str() == other.slot_name.as_str()
&& self.element_field.as_deref() == other.element_field.as_deref()
}
}
impl Eq for StorageValueName {}
impl PartialOrd for StorageValueName {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for StorageValueName {
fn cmp(&self, other: &Self) -> Ordering {
let slot_cmp = self.slot_name.as_str().cmp(other.slot_name.as_str());
if slot_cmp != Ordering::Equal {
return slot_cmp;
}
match (self.element_field.as_deref(), other.element_field.as_deref()) {
(None, None) => Ordering::Equal,
(None, Some(_)) => Ordering::Less,
(Some(_), None) => Ordering::Greater,
(Some(a), Some(b)) => a.cmp(b),
}
}
}
impl FromStr for StorageValueName {
type Err = StorageValueNameError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
if value.is_empty() {
return Err(StorageValueNameError::EmptySuffix);
}
let (slot, field) = match value.split_once('.') {
Some((slot, field)) => {
Self::validate_field_segment(field)?;
if slot.is_empty() || field.is_empty() {
return Err(StorageValueNameError::EmptySuffix);
}
(slot, Some(field))
},
None => (value, None),
};
let slot_name =
StorageSlotName::new(slot).map_err(StorageValueNameError::InvalidSlotName)?;
let field = match field {
Some(field) => {
Self::validate_field_segment(field)?;
Some(field.to_string())
},
None => None,
};
Ok(Self { slot_name, element_field: field })
}
}
impl TryFrom<String> for StorageValueName {
type Error = StorageValueNameError;
fn try_from(value: String) -> Result<Self, Self::Error> {
value.parse()
}
}
impl TryFrom<&str> for StorageValueName {
type Error = StorageValueNameError;
fn try_from(value: &str) -> Result<Self, Self::Error> {
value.parse()
}
}
impl From<StorageValueName> for String {
fn from(value: StorageValueName) -> Self {
value.to_string()
}
}
impl From<&StorageSlotName> for StorageValueName {
fn from(value: &StorageSlotName) -> Self {
StorageValueName::from_slot_name(value)
}
}
impl Display for StorageValueName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.element_field {
None => f.write_str(self.slot_name.as_str()),
Some(field) => {
f.write_str(self.slot_name.as_str())?;
f.write_str(".")?;
f.write_str(field)
},
}
}
}
impl Serializable for StorageValueName {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
let key = self.to_string();
target.write(&key);
}
}
impl Deserializable for StorageValueName {
fn read_from<R: ByteReader>(source: &mut R) -> Result<Self, DeserializationError> {
let key: String = source.read()?;
key.parse().map_err(|err: StorageValueNameError| {
DeserializationError::InvalidValue(err.to_string())
})
}
}
#[derive(Debug, Error)]
pub enum StorageValueNameError {
#[error("key suffix is empty")]
EmptySuffix,
#[error("key segment '{part}' contains invalid character '{character}'")]
InvalidCharacter { part: String, character: char },
#[error("invalid storage slot name")]
InvalidSlotName(#[source] StorageSlotNameError),
}