1use indexmap::IndexMap;
6use schemars::JsonSchema;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
11#[serde(deny_unknown_fields)]
12pub struct TypeDefinition {
13 pub name: String,
14 pub description: String,
15 pub when_to_use: String,
16 #[serde(default)]
17 pub boundaries: Vec<String>,
18 #[serde(default)]
19 pub examples: Vec<TypeExample>,
20 #[serde(default)]
21 pub system_message: Option<String>,
22 pub sections: Vec<SectionDef>,
23 pub metadata_fields: Vec<MetadataFieldDef>,
24 pub title_weight: f32,
25 pub text_fields: Vec<String>,
26 pub hierarchy_relationship: String,
27 #[serde(default)]
28 pub edge_weight_overrides: IndexMap<String, f32>,
29 pub propagating_relationships: Vec<String>,
30 pub updatable_fields: Vec<String>,
31 pub health_required_fields: Vec<String>,
32 pub staleness_threshold_days: u32,
33 pub write_rules: Vec<String>,
34 #[serde(default, skip_serializing_if = "Vec::is_empty")]
43 pub required_outgoing: Vec<RequiredOutgoing>,
44 #[serde(skip)]
48 pub edge_weights: IndexMap<String, f32>,
49}
50
51#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
60#[serde(deny_unknown_fields)]
61pub struct RequiredOutgoing {
62 pub relationships: Vec<String>,
66 pub cardinality: RequiredCardinality,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
73#[serde(rename_all = "snake_case")]
74pub enum RequiredCardinality {
75 AtLeastOne,
76}
77
78impl std::fmt::Display for RequiredCardinality {
79 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80 f.write_str(match self {
81 RequiredCardinality::AtLeastOne => "at_least_one",
82 })
83 }
84}
85
86impl RequiredOutgoing {
87 pub fn admits(&self, outgoing_count: usize) -> bool {
90 match self.cardinality {
91 RequiredCardinality::AtLeastOne => outgoing_count >= 1,
92 }
93 }
94}
95
96impl TypeDefinition {
97 pub fn edge_weight(&self, rel: &str) -> f32 {
101 if let Some(&w) = self.edge_weights.get(rel) {
102 return w;
103 }
104 if let Some(&w) = self.edge_weights.get("_default") {
105 return w;
106 }
107 1.0
108 }
109
110 pub fn section(&self, key: &str) -> Option<&SectionDef> {
111 self.sections.iter().find(|s| s.key == key)
112 }
113
114 pub fn catch_all_section(&self) -> Option<&SectionDef> {
115 self.sections.iter().find(|s| s.catch_all)
116 }
117
118 pub fn metadata_field(&self, key: &str) -> Option<&MetadataFieldDef> {
119 self.metadata_fields.iter().find(|f| f.key == key)
120 }
121
122 pub fn suggest_metadata_field(&self, key: &str) -> Option<String> {
125 crate::schema::closest_match(key, self.metadata_fields.iter().map(|f| f.key.as_str()))
126 }
127
128 pub fn suggest_section(&self, key: &str) -> Option<String> {
132 crate::schema::closest_match(key, self.sections.iter().map(|s| s.key.as_str()))
133 }
134
135 pub fn required_sections(&self) -> impl Iterator<Item = &SectionDef> {
137 self.sections.iter().filter(|s| s.required)
138 }
139
140 pub fn optional_sections(&self) -> impl Iterator<Item = &SectionDef> {
142 self.sections.iter().filter(|s| !s.required)
143 }
144
145 pub fn system_message_str(&self) -> &str {
147 self.system_message.as_deref().unwrap_or("")
148 }
149}
150
151#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
153#[serde(deny_unknown_fields)]
154pub struct TypeExample {
155 pub title: String,
156 pub sections: IndexMap<String, String>,
157}
158
159#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
161#[serde(deny_unknown_fields)]
162pub struct SectionDef {
163 pub key: String,
164 pub heading: String,
165 pub required: bool,
166 pub search_weight: f32,
167 #[serde(default)]
168 pub catch_all: bool,
169 #[serde(default)]
170 pub write_rules: Vec<String>,
171 #[serde(default)]
172 pub description: Option<String>,
173}
174
175#[derive(Debug, Clone, Deserialize, Serialize, JsonSchema)]
177#[serde(deny_unknown_fields)]
178pub struct MetadataFieldDef {
179 pub key: String,
180 pub description: String,
181 pub field_type: FieldType,
182 #[serde(default)]
183 pub default_value: Option<String>,
184 #[serde(default)]
185 pub enum_values: Option<Vec<String>>,
186 #[serde(default)]
187 pub optional: bool,
188 #[serde(default)]
189 pub init_timestamp: bool,
190 #[serde(default)]
191 pub auto_timestamp: bool,
192 #[serde(default)]
193 pub serialization: Serialization,
194 #[serde(default)]
195 pub filterable: Filterable,
196}
197
198#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema)]
199#[serde(rename_all = "snake_case")]
200pub enum FieldType {
201 String,
202 Number,
203 Date,
204 Boolean,
205}
206
207#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema, Default)]
208#[serde(rename_all = "snake_case")]
209pub enum Serialization {
210 #[default]
211 Default,
212 CsvArray,
213 OmitWhenFalsy,
214}
215
216#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize, JsonSchema, Default)]
217#[serde(rename_all = "snake_case")]
218pub enum Filterable {
219 #[default]
220 None,
221 Equality,
222 Range,
223}
224
225impl Filterable {
226 pub fn as_wire_str(self) -> Option<&'static str> {
232 match self {
233 Filterable::None => None,
234 Filterable::Equality => Some("equality"),
235 Filterable::Range => Some("range"),
236 }
237 }
238}