use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Feature {
pub name: String,
pub implies: Vec<FeatureRef>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "kebab-case")]
pub enum FeatureRef {
Local { name: String },
Namespaced { package: String, feature: String },
DepActivation { dep_name: String },
}
impl FeatureRef {
#[must_use]
pub fn parse(s: &str) -> Self {
if let Some(rest) = s.strip_prefix("dep:") {
Self::DepActivation {
dep_name: rest.to_string(),
}
} else if let Some((pkg, feat)) = s.split_once('/') {
Self::Namespaced {
package: pkg.to_string(),
feature: feat.to_string(),
}
} else {
Self::Local {
name: s.to_string(),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_local_feature() {
assert_eq!(
FeatureRef::parse("derive"),
FeatureRef::Local {
name: "derive".into()
}
);
}
#[test]
fn parse_namespaced_feature() {
assert_eq!(
FeatureRef::parse("serde/derive"),
FeatureRef::Namespaced {
package: "serde".into(),
feature: "derive".into(),
}
);
}
#[test]
fn parse_dep_activation() {
assert_eq!(
FeatureRef::parse("dep:some-opt"),
FeatureRef::DepActivation {
dep_name: "some-opt".into()
}
);
}
#[test]
fn round_trip_through_serde() {
let f = Feature {
name: "default".into(),
implies: vec![
FeatureRef::Local { name: "std".into() },
FeatureRef::Namespaced {
package: "serde".into(),
feature: "derive".into(),
},
],
};
let j = serde_json::to_string(&f).unwrap();
let parsed: Feature = serde_json::from_str(&j).unwrap();
assert_eq!(f, parsed);
}
}