use crate::syntax::{
CHAR_COMMA, CHAR_LEFT_PAREN, CHAR_PERIOD, CHAR_RIGHT_PAREN, COMMA_SEPARATOR,
DEFAULT_LANGUAGE_NAME, FEATURE_COMPARISONS_ID, FEATURE_COMPARISONS_SYMBOL,
FEATURE_CONSTRAINTS_ID, FEATURE_CONSTRAINTS_SYMBOL, FEATURE_DISJUNCTION_ID,
FEATURE_DISJUNCTION_SYMBOL, FEATURE_FUNCTIONAL_DEPENDENCIES_ID,
FEATURE_FUNCTIONAL_DEPENDENCIES_SYMBOL, FEATURE_NEGATION_ID, NEGATION_UNICODE_SYMBOL,
PRAGMA_ID_FEATURE, RESERVED_PREFIX,
};
use std::fmt::{Display, Formatter};
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FeatureSet(u32);
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Feature {
label: &'static str,
symbol: &'static str,
mask: u32,
}
pub const FEATURE_NEGATION: Feature = Feature {
label: FEATURE_NEGATION_ID,
symbol: NEGATION_UNICODE_SYMBOL,
mask: 1 << 0,
};
pub const FEATURE_COMPARISONS: Feature = Feature {
label: FEATURE_COMPARISONS_ID,
symbol: FEATURE_COMPARISONS_SYMBOL,
mask: 1 << 1,
};
pub const FEATURE_FUNCTIONAL_DEPENDENCIES: Feature = Feature {
label: FEATURE_FUNCTIONAL_DEPENDENCIES_ID,
symbol: FEATURE_FUNCTIONAL_DEPENDENCIES_SYMBOL,
mask: 1 << 2,
};
pub const FEATURE_DISJUNCTION: Feature = Feature {
label: FEATURE_DISJUNCTION_ID,
symbol: FEATURE_DISJUNCTION_SYMBOL,
mask: 1 << 4,
};
pub const FEATURE_CONSTRAINTS: Feature = Feature {
label: FEATURE_CONSTRAINTS_ID,
symbol: FEATURE_CONSTRAINTS_SYMBOL,
mask: 1 << 5,
};
pub const ALL_FEATURES: &[&Feature] = &[
&FEATURE_NEGATION,
&FEATURE_COMPARISONS,
&FEATURE_CONSTRAINTS,
&FEATURE_DISJUNCTION,
&FEATURE_FUNCTIONAL_DEPENDENCIES,
];
const DEFAULT_FEATURE_SET: u32 = 0;
impl Feature {
pub fn label(&self) -> &'static str {
self.label
}
pub fn symbol(&self) -> &'static str {
self.symbol
}
}
impl Default for FeatureSet {
fn default() -> Self {
Self(DEFAULT_FEATURE_SET)
}
}
impl From<Feature> for FeatureSet {
fn from(v: Feature) -> Self {
Self::from(vec![v])
}
}
impl From<Vec<Feature>> for FeatureSet {
fn from(vs: Vec<Feature>) -> Self {
Self::from(vs.as_slice())
}
}
impl From<&[Feature]> for FeatureSet {
fn from(vs: &[Feature]) -> Self {
Self(vs.iter().fold(0, |mut bits, feat| {
bits |= feat.mask;
bits
}))
}
}
impl Display for FeatureSet {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
if !self.is_default() {
let features = self
.features()
.map(|feat| feat.label().to_string())
.collect::<Vec<String>>();
format!(
"{}{}{}{}{}{}",
RESERVED_PREFIX,
PRAGMA_ID_FEATURE,
CHAR_LEFT_PAREN,
features.join(COMMA_SEPARATOR),
CHAR_RIGHT_PAREN,
CHAR_PERIOD,
)
} else {
String::new()
}
)
}
}
impl FeatureSet {
pub fn is_default(&self) -> bool {
self.0 == DEFAULT_FEATURE_SET
}
pub fn all() -> Self {
Self::from(vec![
FEATURE_NEGATION,
FEATURE_COMPARISONS,
FEATURE_CONSTRAINTS,
FEATURE_DISJUNCTION,
])
}
pub fn supports(&self, feature: &Feature) -> bool {
self.0 & feature.mask == feature.mask
}
pub fn add_support_for(&mut self, feature: &Feature) -> Self {
self.0 |= feature.mask;
*self
}
pub fn remove_support_for(&mut self, feature: &Feature) -> Self {
self.0 &= !feature.mask;
*self
}
pub fn features(&self) -> impl Iterator<Item = &Feature> {
ALL_FEATURES
.iter()
.filter(|feat| self.supports(feat))
.cloned()
}
pub fn language(&self) -> String {
if self.is_default() {
DEFAULT_LANGUAGE_NAME.to_owned()
} else {
format!(
"{}{}{}{}",
CHAR_LEFT_PAREN,
self.features()
.map(|feat| feat.symbol().to_string())
.collect::<Vec<String>>()
.join(&CHAR_COMMA.to_string()),
CHAR_RIGHT_PAREN,
DEFAULT_LANGUAGE_NAME,
)
}
}
}