use indexmap::IndexMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct TypeDefinition {
pub name: String,
pub description: String,
pub when_to_use: String,
#[serde(default)]
pub boundaries: Vec<String>,
#[serde(default)]
pub examples: Vec<TypeExample>,
#[serde(default)]
pub system_message: Option<String>,
pub sections: Vec<SectionDef>,
pub metadata_fields: Vec<MetadataFieldDef>,
pub title_weight: f32,
pub text_fields: Vec<String>,
pub hierarchy_relationship: String,
#[serde(default)]
pub edge_weight_overrides: IndexMap<String, f32>,
pub propagating_relationships: Vec<String>,
pub updatable_fields: Vec<String>,
pub health_required_fields: Vec<String>,
pub staleness_threshold_days: u32,
pub write_rules: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub required_outgoing: Vec<RequiredOutgoing>,
#[serde(skip)]
pub edge_weights: IndexMap<String, f32>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RequiredOutgoing {
pub relationships: Vec<String>,
pub cardinality: RequiredCardinality,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum RequiredCardinality {
AtLeastOne,
}
impl std::fmt::Display for RequiredCardinality {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(match self {
RequiredCardinality::AtLeastOne => "at_least_one",
})
}
}
impl RequiredOutgoing {
pub fn admits(&self, outgoing_count: usize) -> bool {
match self.cardinality {
RequiredCardinality::AtLeastOne => outgoing_count >= 1,
}
}
}
impl TypeDefinition {
pub fn edge_weight(&self, rel: &str) -> f32 {
if let Some(&w) = self.edge_weights.get(rel) {
return w;
}
if let Some(&w) = self.edge_weights.get("_default") {
return w;
}
1.0
}
pub fn section(&self, key: &str) -> Option<&SectionDef> {
self.sections.iter().find(|s| s.key == key)
}
pub fn catch_all_section(&self) -> Option<&SectionDef> {
self.sections.iter().find(|s| s.catch_all)
}
pub fn metadata_field(&self, key: &str) -> Option<&MetadataFieldDef> {
self.metadata_fields.iter().find(|f| f.key == key)
}
pub fn suggest_metadata_field(&self, key: &str) -> Option<String> {
crate::schema::closest_match(key, self.metadata_fields.iter().map(|f| f.key.as_str()))
}
pub fn suggest_section(&self, key: &str) -> Option<String> {
crate::schema::closest_match(key, self.sections.iter().map(|s| s.key.as_str()))
}
pub fn required_sections(&self) -> impl Iterator<Item = &SectionDef> {
self.sections.iter().filter(|s| s.required)
}
pub fn optional_sections(&self) -> impl Iterator<Item = &SectionDef> {
self.sections.iter().filter(|s| !s.required)
}
pub fn system_message_str(&self) -> &str {
self.system_message.as_deref().unwrap_or("")
}
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct TypeExample {
pub title: String,
pub sections: IndexMap<String, String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SectionDef {
pub key: String,
pub heading: String,
pub required: bool,
pub search_weight: f32,
#[serde(default)]
pub catch_all: bool,
#[serde(default)]
pub write_rules: Vec<String>,
#[serde(default)]
pub description: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MetadataFieldDef {
pub key: String,
pub description: String,
pub field_type: FieldType,
#[serde(default)]
pub default_value: Option<String>,
#[serde(default)]
pub enum_values: Option<Vec<String>>,
#[serde(default)]
pub optional: bool,
#[serde(default)]
pub init_timestamp: bool,
#[serde(default)]
pub auto_timestamp: bool,
#[serde(default)]
pub serialization: Serialization,
#[serde(default)]
pub filterable: Filterable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum FieldType {
String,
Number,
Date,
Boolean,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema, Default)]
#[serde(rename_all = "snake_case")]
pub enum Serialization {
#[default]
Default,
CsvArray,
OmitWhenFalsy,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema, Default)]
#[serde(rename_all = "snake_case")]
pub enum Filterable {
#[default]
None,
Equality,
Range,
}
impl Filterable {
pub fn as_wire_str(self) -> Option<&'static str> {
match self {
Filterable::None => None,
Filterable::Equality => Some("equality"),
Filterable::Range => Some("range"),
}
}
}