use type_bridge_core_lib::ast::{Clause, Constraint, FetchItem, Pattern, Statement};
use serde::{Deserialize, Serialize};
use crate::attribute::ValueType;
use crate::error::Result;
use crate::filter::Filter;
use crate::value::AttributeValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum Annotation {
Key,
Unique,
Card(u32, Option<u32>),
Distinct,
}
#[derive(Debug, Clone, Serialize)]
pub struct OwnedAttributeInfo {
pub attr_name: &'static str,
pub value_type: ValueType,
pub annotations: &'static [Annotation],
}
impl OwnedAttributeInfo {
pub fn is_key(&self) -> bool {
self.annotations
.iter()
.any(|a| matches!(a, Annotation::Key))
}
pub fn is_unique(&self) -> bool {
self.annotations
.iter()
.any(|a| matches!(a, Annotation::Unique))
}
pub fn cardinality(&self) -> Option<(u32, Option<u32>)> {
self.annotations.iter().find_map(|a| match a {
Annotation::Card(min, max) => Some((*min, *max)),
_ => None,
})
}
}
pub trait TypeBridgeEntity: Sized + Send + Sync + 'static {
const TYPE_NAME: &'static str;
const IS_ABSTRACT: bool = false;
const PARENT_TYPE: Option<&'static str> = None;
fn owned_attributes() -> &'static [OwnedAttributeInfo];
fn iid(&self) -> Option<&str>;
fn set_iid(&mut self, iid: String);
fn to_attribute_values(&self) -> Vec<(&'static str, AttributeValue)>;
fn from_document(doc: &serde_json::Map<String, serde_json::Value>) -> Result<Self>;
fn to_insert_clauses(&self, var: &str) -> Vec<Clause> {
let mut statements = vec![Statement::Isa {
variable: var.to_string(),
type_name: Self::TYPE_NAME.to_string(),
}];
for (attr_name, value) in self.to_attribute_values() {
statements.push(Statement::Has {
subject_var: var.to_string(),
attr_name: attr_name.to_string(),
value: value.to_ast_value(),
});
}
vec![Clause::Insert(statements)]
}
fn to_insert_with_iid_fetch(&self, var: &str) -> Vec<Clause> {
let mut clauses = self.to_insert_clauses(var);
clauses.push(Clause::Fetch(vec![FetchItem::Function {
key: "iid".to_string(),
func_name: "iid".to_string(),
var: var.to_string(),
}]));
clauses
}
fn identification_constraints(&self) -> Vec<Constraint> {
if let Some(iid) = self.iid() {
return vec![Constraint::Iid(iid.to_string())];
}
let key_attrs: Vec<&'static str> = Self::owned_attributes()
.iter()
.filter(|a| a.is_key())
.map(|a| a.attr_name)
.collect();
self.to_attribute_values()
.into_iter()
.filter(|(name, _)| key_attrs.contains(name))
.map(|(attr_name, value)| Constraint::Has {
attr_name: attr_name.to_string(),
value: value.to_ast_value(),
})
.collect()
}
fn to_match_pattern(&self, var: &str) -> Pattern {
Pattern::Entity {
variable: var.to_string(),
type_name: Self::TYPE_NAME.to_string(),
constraints: self.identification_constraints(),
is_strict: false,
}
}
fn build_polymorphic_fetch(var: &str, type_name: &str, filters: &[Filter]) -> Vec<Clause> {
let constraints: Vec<Constraint> = filters
.iter()
.map(|f| Constraint::Has {
attr_name: f.attr_name.clone(),
value: f.value.to_ast_value(),
})
.collect();
let match_patterns = vec![
Pattern::Entity {
variable: var.to_string(),
type_name: "$t".to_string(),
constraints,
is_strict: true,
},
Pattern::SubType {
variable: "$t".to_string(),
parent_type: type_name.to_string(),
},
];
let fetch_items = vec![
FetchItem::Function {
key: "_iid".to_string(),
func_name: "iid".to_string(),
var: var.to_string(),
},
FetchItem::Function {
key: "_type".to_string(),
func_name: "label".to_string(),
var: "$t".to_string(),
},
FetchItem::NestedWildcard {
key: "attributes".to_string(),
var: var.to_string(),
},
];
vec![Clause::Match(match_patterns), Clause::Fetch(fetch_items)]
}
}