use std::collections::{BTreeMap, BTreeSet};
use brk_types::Index;
use super::PatternMode;
#[derive(Debug, Clone)]
pub struct IndexSetPattern {
pub name: String,
pub indexes: BTreeSet<Index>,
}
#[derive(Debug, Clone)]
pub struct StructuralPattern {
pub name: String,
pub fields: Vec<PatternField>,
pub mode: Option<PatternMode>,
pub is_generic: bool,
}
impl StructuralPattern {
pub fn is_parameterizable(&self) -> bool {
self.mode.is_some()
}
pub fn get_field_part(&self, field_name: &str) -> Option<&str> {
match &self.mode {
Some(PatternMode::Suffix { relatives }) => {
relatives.get(field_name).map(|s| s.as_str())
}
Some(PatternMode::Prefix { prefixes }) => prefixes.get(field_name).map(|s| s.as_str()),
Some(PatternMode::Templated { templates }) => {
templates.get(field_name).map(|s| s.as_str())
}
None => None,
}
}
pub fn is_suffix_mode(&self) -> bool {
matches!(
&self.mode,
Some(PatternMode::Suffix { .. } | PatternMode::Templated { .. })
)
}
pub fn is_templated(&self) -> bool {
matches!(&self.mode, Some(PatternMode::Templated { .. }))
}
pub fn extract_disc_from_instance(
&self,
instance_field_parts: &BTreeMap<String, String>,
) -> Option<String> {
let templates = match &self.mode {
Some(PatternMode::Templated { templates }) => templates,
_ => return None,
};
for (field_name, template) in templates {
if let Some(value) = instance_field_parts.get(field_name)
&& let Some(disc) = extract_disc(template, value)
{
return Some(disc.trim_start_matches('_').to_string());
}
}
Some(String::new())
}
pub fn field_parts_match(&self, instance_field_parts: &BTreeMap<String, String>) -> bool {
match &self.mode {
Some(PatternMode::Suffix { relatives }) => {
relatives.iter().all(|(field_name, pattern_suffix)| {
instance_field_parts
.get(field_name)
.is_some_and(|instance_suffix| instance_suffix == pattern_suffix)
})
}
Some(PatternMode::Prefix { prefixes }) => {
prefixes.iter().all(|(field_name, pattern_prefix)| {
instance_field_parts
.get(field_name)
.is_some_and(|instance_prefix| instance_prefix == pattern_prefix)
})
}
Some(PatternMode::Templated { templates }) => {
let first_template_field = templates.iter().next();
let Some((ref_field, ref_template)) = first_template_field else {
return false;
};
let Some(ref_value) = instance_field_parts.get(ref_field) else {
return false;
};
let Some(disc) = extract_disc(ref_template, ref_value) else {
return false;
};
templates.iter().all(|(field_name, template)| {
instance_field_parts
.get(field_name)
.is_some_and(|value| *value == template.replace("{disc}", &disc))
})
}
None => false,
}
}
}
fn extract_disc(template: &str, value: &str) -> Option<String> {
let (prefix, suffix) = template.split_once("{disc}")?;
if value.starts_with(prefix) && value.ends_with(suffix) {
let disc = &value[prefix.len()..value.len() - suffix.len()];
if !disc.is_empty() {
return Some(disc.to_string());
}
}
None
}
#[derive(Debug, Clone, PartialOrd, Ord)]
pub struct PatternField {
pub name: String,
pub rust_type: String,
pub json_type: String,
pub indexes: BTreeSet<Index>,
pub type_param: Option<String>,
}
impl PatternField {
pub fn is_leaf(&self) -> bool {
!self.indexes.is_empty()
}
pub fn is_branch(&self) -> bool {
self.indexes.is_empty()
}
}
impl std::hash::Hash for PatternField {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.rust_type.hash(state);
self.json_type.hash(state);
self.indexes.hash(state);
}
}
impl PartialEq for PatternField {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
&& self.rust_type == other.rust_type
&& self.json_type == other.json_type
&& self.indexes == other.indexes
}
}
impl Eq for PatternField {}