use crate::{Error, Platform, TargetFeatures};
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, collections::BTreeSet};
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case")]
pub struct PlatformSummary {
pub triple: String,
#[serde(with = "target_features_impl")]
pub target_features: TargetFeaturesSummary,
#[serde(skip_serializing_if = "BTreeSet::is_empty", default)]
pub flags: BTreeSet<String>,
}
impl PlatformSummary {
pub fn new(platform: &Platform<'_>) -> Result<Self, Error> {
if platform.is_custom() {
return Err(Error::CustomPlatformSummary);
};
Ok(Self {
triple: platform.triple().to_string(),
target_features: TargetFeaturesSummary::new(platform.target_features()),
flags: platform.flags().map(|flag| flag.to_string()).collect(),
})
}
pub fn to_platform(&self) -> Result<Platform<'static>, Error> {
let mut platform = Platform::new(&self.triple, self.target_features.to_target_features())?;
platform.add_flags(self.flags.iter().cloned());
Ok(platform)
}
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "kebab-case", untagged)]
#[non_exhaustive]
pub enum TargetFeaturesSummary {
Unknown,
Features(BTreeSet<String>),
All,
}
impl TargetFeaturesSummary {
pub fn new(target_features: &TargetFeatures) -> Self {
match target_features {
TargetFeatures::Unknown => TargetFeaturesSummary::Unknown,
TargetFeatures::Features(features) => TargetFeaturesSummary::Features(
features.iter().map(|feature| feature.to_string()).collect(),
),
TargetFeatures::All => TargetFeaturesSummary::All,
}
}
pub fn to_target_features(&self) -> TargetFeatures {
match self {
TargetFeaturesSummary::Unknown => TargetFeatures::Unknown,
TargetFeaturesSummary::All => TargetFeatures::All,
TargetFeaturesSummary::Features(features) => {
let features = features
.iter()
.map(|feature| Cow::Owned(feature.clone()))
.collect();
TargetFeatures::Features(features)
}
}
}
}
mod target_features_impl {
use super::*;
use serde::{de::Error, Deserializer, Serializer};
pub fn serialize<S>(
target_features: &TargetFeaturesSummary,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match target_features {
TargetFeaturesSummary::Unknown => "unknown".serialize(serializer),
TargetFeaturesSummary::All => "all".serialize(serializer),
TargetFeaturesSummary::Features(features) => features.serialize(serializer),
}
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<TargetFeaturesSummary, D::Error>
where
D: Deserializer<'de>,
{
let d = TargetFeaturesDeserialize::deserialize(deserializer)?;
match d {
TargetFeaturesDeserialize::String(target_features) => match target_features.as_str() {
"unknown" => Ok(TargetFeaturesSummary::Unknown),
"all" => Ok(TargetFeaturesSummary::All),
other => Err(D::Error::custom(format!(
"unknown string for target features: {}",
other,
))),
},
TargetFeaturesDeserialize::List(target_features) => {
Ok(TargetFeaturesSummary::Features(target_features))
}
}
}
#[derive(Deserialize)]
#[serde(untagged)]
enum TargetFeaturesDeserialize {
String(String),
List(BTreeSet<String>),
}
}
#[cfg(all(test, feature = "proptest1"))]
mod tests {
use super::*;
use proptest::prelude::*;
use std::collections::HashSet;
proptest! {
#[test]
fn summary_roundtrip(platform in Platform::strategy(any::<TargetFeatures>())) {
let summary = PlatformSummary::new(&platform).expect("Platform::strategy does not generate custom platforms");
let serialized = toml::ser::to_string(&summary).expect("serialization succeeded");
let deserialized: PlatformSummary = toml::from_str(&serialized).expect("deserialization succeeded");
assert_eq!(summary, deserialized, "summary and deserialized should match");
let platform2 = deserialized.to_platform().expect("conversion to Platform succeeded");
assert_eq!(platform.triple(), platform2.triple(), "triples match");
assert_eq!(platform.target_features(), platform2.target_features(), "target features match");
assert_eq!(platform.flags().collect::<HashSet<_>>(), platform2.flags().collect::<HashSet<_>>(), "flags match");
}
}
}