use super::Setting;
use serde::{
de::{self, MapAccess, Visitor},
Deserialize, Deserializer,
};
use std::str::FromStr;
use thiserror::Error;
impl<'de> Deserialize<'de> for Setting {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Debug, Deserialize, Hash, PartialEq, Eq)]
#[serde(field_identifier, rename_all = "camelCase")]
enum Field {
Id,
Label,
Summary,
Hidden,
Advanced,
Group,
Value,
Default,
r#Type,
EnumValues,
}
struct SettingVisitor;
impl<'de> Visitor<'de> for SettingVisitor {
type Value = Setting;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("struct Setting")
}
fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
where
V: MapAccess<'de>,
{
let mut id = None;
let mut label = None;
let mut summary = None;
let mut hidden = None;
let mut advanced = None;
let mut group = None;
let mut value = None;
let mut default = None;
let mut r#type = None;
let mut enum_values: Option<serde_json::Value> = None;
while let Some(key) = map.next_key()? {
match key {
Field::Id => {
if id.is_some() {
return Err(de::Error::duplicate_field("id"));
}
id = Some(map.next_value()?);
}
Field::Label => {
if label.is_some() {
return Err(de::Error::duplicate_field("label"));
}
label = Some(map.next_value()?);
}
Field::Summary => {
if summary.is_some() {
return Err(de::Error::duplicate_field("summary"));
}
summary = Some(map.next_value()?);
}
Field::Hidden => {
if hidden.is_some() {
return Err(de::Error::duplicate_field("hidden"));
}
hidden = Some(map.next_value()?);
}
Field::Advanced => {
if advanced.is_some() {
return Err(de::Error::duplicate_field("advanced"));
}
advanced = Some(map.next_value()?);
}
Field::Group => {
if group.is_some() {
return Err(de::Error::duplicate_field("group"));
}
group = Some(map.next_value()?);
}
Field::Value => {
if value.is_some() {
return Err(de::Error::duplicate_field("value"));
}
value = Some(map.next_value()?);
}
Field::Default => {
if default.is_some() {
return Err(de::Error::duplicate_field("default"));
}
default = Some(map.next_value()?);
}
Field::Type => {
if r#type.is_some() {
return Err(de::Error::duplicate_field("type"));
}
r#type = Some(map.next_value()?);
}
Field::EnumValues => {
if enum_values.is_some() {
return Err(de::Error::duplicate_field("enumValues"));
}
enum_values = Some(map.next_value()?);
}
}
}
let id: String = id.ok_or_else(|| de::Error::missing_field("id"))?;
let label: String = label.ok_or_else(|| de::Error::missing_field("label"))?;
let summary: String = summary.ok_or_else(|| de::Error::missing_field("summary"))?;
let hidden: bool = hidden.ok_or_else(|| de::Error::missing_field("hidden"))?;
let advanced: bool =
advanced.ok_or_else(|| de::Error::missing_field("advanced"))?;
let group: String = group.ok_or_else(|| de::Error::missing_field("group"))?;
let r#type: String = r#type.ok_or_else(|| de::Error::missing_field("type"))?;
let value: serde_json::Value =
value.ok_or_else(|| de::Error::missing_field("value"))?;
let default: serde_json::Value =
default.ok_or_else(|| de::Error::missing_field("default"))?;
let mut suggested_values: Option<Vec<super::SettingEnumValue>> = None;
if let Some(serde_json::Value::String(enum_values)) = enum_values {
let suggested_values_tmp: Result<Vec<_>, _> = enum_values
.split('|')
.map(super::SettingEnumValue::from_str)
.collect();
if let Ok(ok) = suggested_values_tmp {
suggested_values = Some(ok);
} else {
return Err(de::Error::custom("invalid value inside enumValues"));
}
}
match r#type.as_str() {
"bool" => Ok(Setting {
id,
label,
summary,
hidden,
advanced,
group,
value: super::Value::Bool(
value
.as_bool()
.ok_or_else(|| de::Error::custom("bool expected"))?,
),
default: super::Value::Bool(
default
.as_bool()
.ok_or_else(|| de::Error::custom("bool expected"))?,
),
suggested_values,
}),
"int" => {
fn int_or_err<E>(v: serde_json::Value, id: &str) -> Result<i64, E>
where
E: de::Error,
{
match v {
serde_json::Value::Number(n) => n
.as_i64()
.ok_or_else(|| de::Error::custom(format_args!("invalid value for {id}: {n}, expected numeric or numeric string"))),
serde_json::Value::String(s) => s
.parse::<i64>()
.map_err(|_| de::Error::custom(format_args!("invalid value for {id}: \"{s}\", expected numeric or numeric string"))),
_ => Err(de::Error::custom(format_args!("invalid value for {id}: {v:?}, expected numeric or numeric string"))),
}
}
let value: i64 = int_or_err(value, &id)?;
let default: i64 = int_or_err(default, &id)?;
Ok(Setting {
id,
label,
summary,
hidden,
advanced,
group,
value: super::Value::Int(value),
default: super::Value::Int(default),
suggested_values,
})
}
"text" => Ok(Setting {
id,
label,
summary,
hidden,
advanced,
group,
value: super::Value::Text(
value
.as_str()
.ok_or_else(|| de::Error::custom("string expected"))?
.to_owned(),
),
default: super::Value::Text(
default
.as_str()
.ok_or_else(|| de::Error::custom("string expected"))?
.to_owned(),
),
suggested_values,
}),
"double" => {
fn double_or_err<E>(v: serde_json::Value, id: &str) -> Result<f64, E>
where
E: de::Error,
{
match v {
serde_json::Value::Number(n) => n
.as_f64()
.ok_or_else(|| de::Error::custom(format_args!("invalid value for {id}: {n}, expected numeric or numeric string"))),
serde_json::Value::String(s) => s
.parse::<f64>()
.map_err(|_| de::Error::custom(format_args!("invalid value for {id}: \"{s}\", expected numeric or numeric string"))),
_ => Err(de::Error::custom(format_args!("invalid value for {id}: {v:?}, expected numeric or numeric string"))),
}
}
let value: f64 = double_or_err(value, &id)?;
let default: f64 = double_or_err(default, &id)?;
Ok(Setting {
id,
label,
summary,
hidden,
advanced,
group,
value: super::Value::Double(value),
default: super::Value::Double(default),
suggested_values,
})
}
_ => Err(de::Error::unknown_variant(
&r#type,
&["bool", "int", "text", "double"],
)),
}
}
}
const FIELDS: &[&str] = &[
"id",
"label",
"summary",
"hidden",
"advanced",
"group",
"value",
"default",
"type",
"enumValues",
];
deserializer.deserialize_struct("Setting", FIELDS, SettingVisitor)
}
}
#[derive(Error, Debug)]
pub enum SettingError {
#[error("Provided `enumValues` string incorrectly formatted")]
IncorrectEnumValuesFormat,
}
impl std::str::FromStr for super::SettingEnumValue {
type Err = SettingError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let value: Vec<_> = s.split(':').collect();
match value.len() {
1 => Ok(super::SettingEnumValue {
value: String::from(s),
hint: String::from(s),
}),
2 => Ok(super::SettingEnumValue {
value: String::from(value[0]),
hint: String::from(value[1]),
}),
_ => Err(SettingError::IncorrectEnumValuesFormat),
}
}
}