#[derive(Deserialize, Debug, Clone)]
struct FeatureDetail
{
#[serde(default)] title: String,
#[serde(default)] description: String,
#[serde(with = "url_serde", rename = "spec")] specification_url: Url,
#[serde(default)] status: Status,
#[serde(default)] links: Vec<Link>,
#[serde(default)] bugs: Vec<Bug>,
#[serde(default)] categories: Vec<Category>,
#[serde(default, rename = "stats")] implementations_by_agents: HashMap<AgentName, BTreeMap<Version, SupportDetail>>,
#[serde(default)] notes: String,
#[serde(default, rename = "notes_by_num")] notes_by_one_based_number: BTreeMap<u8, String>,
#[serde(default, deserialize_with = "FeatureDetail::deserialize_parent")] parent: Option<FeatureName>,
#[serde(default, rename="usage_perc_y")] supported_by_default_usage: UsagePercentage,
#[serde(default, rename="usage_perc_a")] almost_supported_usage: UsagePercentage,
#[serde(default, rename="ucprefix")] upper_case_prefix: bool,
#[serde(default, deserialize_with = "FeatureDetail::deserialize_comma_separated_strings")] keywords: Vec<String>,
#[serde(default, deserialize_with = "FeatureDetail::deserialize_comma_separated_strings", rename="ie_id")] internet_explorer_feature_identifiers: Vec<String>,
#[serde(default, deserialize_with = "FeatureDetail::deserialize_comma_separated_strings", rename="chrome_id")] blink_feature_identifiers: Vec<String>,
#[serde(default, deserialize_with = "FeatureDetail::deserialize_comma_separated_strings", rename="firefox_id")] firefox_feature_identifiers: Vec<String>,
#[serde(default, deserialize_with = "FeatureDetail::deserialize_comma_separated_strings", rename="webkit_id")] webkit_feature_identifiers: Vec<String>,
#[serde(default = "FeatureDetail::shown_default")] shown: bool,
}
impl FeatureDetail
{
#[inline(always)]
fn deserialize_parent<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Option<FeatureName>, D::Error>
{
struct ParentVisitor;
impl<'de> Visitor<'de> for ParentVisitor
{
type Value = Option<FeatureName>;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result
{
formatter.write_str("a string which contains comma separated sub-strings")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E>
{
if v.is_empty()
{
Ok(None)
}
else
{
Ok(Some(FeatureName(v.to_owned())))
}
}
fn visit_string<E: de::Error>(self, v: String) -> Result<Self::Value, E>
{
if v.is_empty()
{
Ok(None)
}
else
{
Ok(Some(FeatureName(v)))
}
}
}
deserializer.deserialize_str(ParentVisitor)
}
#[inline(always)]
fn deserialize_comma_separated_strings<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<String>, D::Error>
{
struct CommaSeparatedStringsVisitor;
impl<'de> Visitor<'de> for CommaSeparatedStringsVisitor
{
type Value = Vec<String>;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result
{
formatter.write_str("a string which contains comma separated sub-strings")
}
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E>
{
if v.is_empty()
{
return Ok(vec![]);
}
let mut strings = Vec::with_capacity(16);
for string in v.split(',')
{
let trimmed = string.trim();
if !trimmed.is_empty()
{
strings.push(trimmed.to_owned());
}
}
strings.shrink_to_fit();
Ok(strings)
}
}
deserializer.deserialize_str(CommaSeparatedStringsVisitor)
}
#[inline(always)]
fn shown_default() -> bool
{
true
}
}