use crate::utils::fmt;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Features(FeaturesInner);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) enum FeaturesInner {
Only(&'static str),
Any(Vec<&'static str>),
All(Vec<&'static str>),
Custom { custom: &'static str, plural: bool },
}
impl Features {
pub fn new(feature: &'static str) -> Self {
Self(FeaturesInner::Only(feature))
}
pub fn any<I>(itr: I) -> Self
where
I: IntoIterator<Item = &'static str>,
{
Self(FeaturesInner::Any(itr.into_iter().collect()))
}
pub fn all<I>(itr: I) -> Self
where
I: IntoIterator<Item = &'static str>,
{
Self(FeaturesInner::All(itr.into_iter().collect()))
}
pub fn custom(custom: &'static str, plural: bool) -> Self {
Self(FeaturesInner::Custom { custom, plural })
}
#[cfg(any(test, feature = "test-app"))]
pub(crate) fn satisfied_by(&self, enabled: &std::collections::HashSet<String>) -> bool {
match &self.0 {
FeaturesInner::Only(feature) => enabled.contains(*feature),
FeaturesInner::Any(any) => any.iter().any(|f| enabled.contains(*f)),
FeaturesInner::All(all) => all.iter().all(|f| enabled.contains(*f)),
FeaturesInner::Custom { .. } => false,
}
}
}
impl From<&'static str> for Features {
fn from(feature: &'static str) -> Features {
Features::new(feature)
}
}
impl std::fmt::Display for Features {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn feature(plural: bool) -> &'static str {
if plural {
"features"
} else {
"feature"
}
}
match &self.0 {
FeaturesInner::Only(feature) => write!(f, "feature \"{}\"", feature),
FeaturesInner::Any(any) => {
if any.is_empty() {
f.write_str("feature <missing>")
} else {
write!(
f,
"{} {}",
feature(any.len() != 1),
fmt::Delimit::new(any.iter().map(fmt::Quote), ", ")
.with_last(", or ")
.with_pair(" or ")
)
}
}
FeaturesInner::All(all) => {
if all.is_empty() {
f.write_str("feature <missing>")
} else {
write!(
f,
"{} {}",
feature(all.len() != 1),
fmt::Delimit::new(all.iter().map(fmt::Quote), ", ")
.with_last(", and ")
.with_pair(" and ")
)
}
}
FeaturesInner::Custom { custom, plural } => {
write!(f, "{} {}", feature(*plural), custom)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_rendering() {
let list = [
(Features::new("a"), "feature \"a\""),
(Features::from("b"), "feature \"b\""),
(Features::any([]), "feature <missing>"),
(Features::any(["a"]), "feature \"a\""),
(Features::any(["a", "b"]), "features \"a\" or \"b\""),
(
Features::any(["a", "b", "c"]),
"features \"a\", \"b\", or \"c\"",
),
(Features::all([]), "feature <missing>"),
(Features::all(["a"]), "feature \"a\""),
(Features::all(["a", "b"]), "features \"a\" and \"b\""),
(
Features::all(["a", "b", "c"]),
"features \"a\", \"b\", and \"c\"",
),
(
Features::custom("custom/stuff", false),
"feature custom/stuff",
),
(
Features::custom("custom/stuff", true),
"features custom/stuff",
),
];
for (feature, expected) in list.iter() {
assert_eq!(feature.to_string(), *expected, "Failed for {:?}", feature);
}
}
}