mod case;
mod patterns;
mod schema;
mod tree;
pub use case::*;
pub use schema::*;
pub use tree::*;
use std::collections::{BTreeSet, HashMap};
use brk_query::Vecs;
use brk_types::Index;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FieldNamePosition {
Prepend(String),
Append(String),
Identity,
SetBase(String),
}
#[derive(Debug)]
pub struct ClientMetadata {
pub catalog: brk_types::TreeNode,
pub structural_patterns: Vec<StructuralPattern>,
pub used_indexes: BTreeSet<Index>,
pub index_set_patterns: Vec<IndexSetPattern>,
pub concrete_to_pattern: HashMap<Vec<PatternField>, String>,
}
impl ClientMetadata {
pub fn from_vecs(vecs: &Vecs) -> Self {
let catalog = vecs.catalog().clone();
let (structural_patterns, concrete_to_pattern) =
patterns::detect_structural_patterns(&catalog);
let (used_indexes, index_set_patterns) = tree::detect_index_patterns(&catalog);
ClientMetadata {
catalog,
structural_patterns,
used_indexes,
index_set_patterns,
concrete_to_pattern,
}
}
pub fn find_index_set_pattern(&self, indexes: &BTreeSet<Index>) -> Option<&IndexSetPattern> {
self.index_set_patterns
.iter()
.find(|p| &p.indexes == indexes)
}
pub fn is_pattern_type(&self, type_name: &str) -> bool {
self.structural_patterns.iter().any(|p| p.name == type_name)
}
pub fn find_pattern(&self, name: &str) -> Option<&StructuralPattern> {
self.structural_patterns.iter().find(|p| p.name == name)
}
pub fn is_pattern_generic(&self, name: &str) -> bool {
self.find_pattern(name).is_some_and(|p| p.is_generic)
}
pub fn get_generic_value_type(
&self,
pattern_name: &str,
fields: &[PatternField],
) -> Option<String> {
if !self.is_pattern_generic(pattern_name) {
return None;
}
fields
.iter()
.find(|f| f.is_leaf())
.map(|f| extract_inner_type(&f.rust_type))
}
pub fn pattern_lookup(&self) -> HashMap<Vec<PatternField>, String> {
let mut lookup = self.concrete_to_pattern.clone();
for p in &self.structural_patterns {
lookup.insert(p.fields.clone(), p.name.clone());
}
lookup
}
pub fn field_uses_accessor(&self, field: &PatternField) -> bool {
self.find_index_set_pattern(&field.indexes).is_some()
}
}
#[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 field_positions: HashMap<String, FieldNamePosition>,
pub is_generic: bool,
}
impl StructuralPattern {
pub fn contains_leaves(&self) -> bool {
self.fields.iter().any(|f| f.is_leaf())
}
pub fn is_parameterizable(&self) -> bool {
!self.field_positions.is_empty()
&& self
.fields
.iter()
.all(|f| f.is_branch() || self.field_positions.contains_key(&f.name))
}
pub fn get_field_position(&self, field_name: &str) -> Option<&FieldNamePosition> {
self.field_positions.get(field_name)
}
}
#[derive(Debug, Clone, PartialOrd, Ord)]
pub struct PatternField {
pub name: String,
pub rust_type: String,
pub json_type: String,
pub indexes: BTreeSet<Index>,
}
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);
}
}
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
}
}
impl Eq for PatternField {}