use alloc::string::{String, ToString};
use serde::Deserialize;
use thiserror::Error;
use super::super::{
InitStorageData,
InitStorageDataError as CoreInitStorageDataError,
StorageValueName,
StorageValueNameError,
WordValue,
};
use super::RawMapEntrySchema;
impl InitStorageData {
pub fn from_toml(toml_str: &str) -> Result<Self, InitStorageDataError> {
let table: toml::Table = toml::from_str(toml_str)?;
let mut data = InitStorageData::default();
for (key, value) in table {
let name: StorageValueName =
key.parse().map_err(InitStorageDataError::InvalidStorageValueName)?;
match value {
toml::Value::Table(nested) => {
if nested.is_empty() {
return Err(InitStorageDataError::EmptyTable(name.to_string()));
}
if name.field_name().is_some() {
return Err(InitStorageDataError::ExcessiveNesting(name.to_string()));
}
for (field, field_value) in nested {
let field_name =
StorageValueName::from_slot_name_with_suffix(name.slot_name(), &field)
.map_err(InitStorageDataError::InvalidStorageValueName)?;
let word = WordValue::deserialize(field_value).map_err(|_| {
InitStorageDataError::InvalidValue(field_name.to_string())
})?;
data.insert_value(field_name, word)?;
}
},
toml::Value::Array(items)
if items.iter().all(|v| matches!(v, toml::Value::Table(_))) =>
{
if name.field_name().is_some() {
return Err(InitStorageDataError::InvalidMapEntryKey(name.to_string()));
}
for item in items {
let entry: RawMapEntrySchema = RawMapEntrySchema::deserialize(item)
.map_err(|e| {
InitStorageDataError::InvalidMapEntrySchema(e.to_string())
})?;
data.insert_map_entry(name.slot_name().clone(), entry.key, entry.value)?;
}
},
other => {
let word = WordValue::deserialize(other)
.map_err(|_| InitStorageDataError::InvalidValue(name.to_string()))?;
data.insert_value(name, word)?;
},
}
}
Ok(data)
}
}
#[derive(Debug, Error)]
pub enum InitStorageDataError {
#[error("failed to parse TOML: {0}")]
InvalidToml(#[from] toml::de::Error),
#[error("empty table encountered for key `{0}`")]
EmptyTable(String),
#[error(transparent)]
InvalidData(#[from] CoreInitStorageDataError),
#[error("invalid map entry key `{0}`: map entries must target a slot name")]
InvalidMapEntryKey(String),
#[error("excessive nesting for key `{0}`: only one level of table nesting is allowed")]
ExcessiveNesting(String),
#[error(
"invalid input for `{0}`: expected a string, a 4-element string array, or a map entry list"
)]
InvalidValue(String),
#[error("invalid storage value name")]
InvalidStorageValueName(#[source] StorageValueNameError),
#[error("invalid map entry: {0}")]
InvalidMapEntrySchema(String),
}