use energyml_geometry::shape::Shape;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Selector {
Any,
Contains(String),
}
impl Selector {
pub fn parse(pattern: &str) -> Selector {
match pattern.trim() {
"*" | "" => Selector::Any,
other => Selector::Contains(other.to_ascii_lowercase()),
}
}
pub fn matches(&self, qualified_type: &str) -> bool {
match self {
Selector::Any => true,
Selector::Contains(needle) => qualified_type.to_ascii_lowercase().contains(needle.as_str()),
}
}
}
impl std::fmt::Display for Selector {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Selector::Any => f.write_str("*"),
Selector::Contains(needle) => f.write_str(needle),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Shapes {
rules: Vec<(Selector, Shape)>,
}
impl Shapes {
pub fn new() -> Self {
Self::default()
}
pub fn with(mut self, selector: Selector, shape: Shape) -> Self {
self.rules.push((selector, shape));
self
}
pub fn parse_rule(self, rule: &str) -> std::result::Result<Self, String> {
let Some((pattern, shape)) = rule.split_once('=') else {
return Err(format!(
"`{rule}` is not a rule: write it as PATTERN=SHAPE, e.g. `Grid2d=surface`"
));
};
let Some(shape) = Shape::parse(shape) else {
let names: Vec<&str> = Shape::all().iter().map(|s| s.name()).collect();
return Err(format!(
"`{shape}` is not a shape - try one of: {}",
names.join(", ")
));
};
Ok(self.with(Selector::parse(pattern), shape))
}
pub fn shape_for(&self, qualified_type: &str) -> Shape {
self.rules
.iter()
.find(|(selector, _)| selector.matches(qualified_type))
.map(|(_, shape)| *shape)
.unwrap_or_default()
}
pub fn is_empty(&self) -> bool {
self.rules.is_empty()
}
pub fn rules(&self) -> &[(Selector, Shape)] {
&self.rules
}
}