1use crate::SymbolId;
10use std::collections::{BTreeMap, BTreeSet};
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub struct SemanticSchema {
15 pub id: SymbolId,
16 pub name: String,
17 pub description: String,
18
19 pub functions: BTreeMap<SymbolId, SemanticFunction>,
21
22 pub types: BTreeMap<SymbolId, SemanticType>,
24
25 pub symbol_table: SymbolTable,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31pub struct SemanticFunction {
32 pub id: SymbolId,
33 pub name: String,
34 pub path: String,
35 pub description: String,
36 pub deprecation_note: Option<String>,
37
38 pub input_type: Option<SymbolId>,
40 pub input_headers: Option<SymbolId>,
41 pub output_type: SemanticOutputType,
42 pub error_type: Option<SymbolId>,
43
44 pub serialization: Vec<crate::SerializationMode>,
45 pub readonly: bool,
46 pub tags: BTreeSet<String>,
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum SemanticOutputType {
51 Complete(Option<SymbolId>),
52 Stream { item_type: SymbolId },
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum SemanticType {
58 Primitive(SemanticPrimitive),
59 Struct(SemanticStruct),
60 Enum(SemanticEnum),
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
64pub struct SemanticPrimitive {
65 pub id: SymbolId,
66 pub name: String,
67 pub original_name: String,
68 pub description: String,
69
70 pub parameters: Vec<SemanticTypeParameter>,
72
73 pub fallback: Option<SymbolId>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
78pub struct SemanticStruct {
79 pub id: SymbolId,
80 pub name: String,
81 pub original_name: String,
82 pub serde_name: String,
83 pub description: String,
84
85 pub parameters: Vec<SemanticTypeParameter>,
87
88 pub fields: BTreeMap<SymbolId, SemanticField>,
90
91 pub transparent: bool,
93 pub is_tuple: bool,
94 pub is_unit: bool,
95
96 pub codegen_config: crate::LanguageSpecificTypeCodegenConfig,
98}
99
100#[derive(Debug, Clone, PartialEq, Eq)]
101pub struct SemanticEnum {
102 pub id: SymbolId,
103 pub name: String,
104 pub original_name: String,
105 pub serde_name: String,
106 pub description: String,
107
108 pub parameters: Vec<SemanticTypeParameter>,
110
111 pub variants: BTreeMap<SymbolId, SemanticVariant>,
113
114 pub representation: crate::Representation,
116
117 pub codegen_config: crate::LanguageSpecificTypeCodegenConfig,
119}
120
121#[derive(Debug, Clone, PartialEq, Eq)]
122pub struct SemanticField {
123 pub id: SymbolId,
124 pub name: String,
125 pub serde_name: String,
126 pub description: String,
127 pub deprecation_note: Option<String>,
128
129 pub type_ref: ResolvedTypeReference,
131
132 pub required: bool,
134 pub flattened: bool,
135
136 pub transform_callback: String,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
141pub struct SemanticVariant {
142 pub id: SymbolId,
143 pub name: String,
144 pub serde_name: String,
145 pub description: String,
146
147 pub fields: BTreeMap<SymbolId, SemanticField>,
149
150 pub discriminant: Option<isize>,
152 pub untagged: bool,
153 pub field_style: FieldStyle,
154}
155
156#[derive(Debug, Clone, PartialEq, Eq)]
157pub enum FieldStyle {
158 Named,
159 Unnamed,
160 Unit,
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
164pub struct SemanticTypeParameter {
165 pub name: String,
166 pub description: String,
167
168 pub bounds: Vec<SymbolId>,
170 pub default: Option<SymbolId>,
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
175pub struct ResolvedTypeReference {
176 pub target: SymbolId,
178
179 pub arguments: Vec<ResolvedTypeReference>,
181
182 pub original_name: String,
184}
185
186#[derive(Debug, Clone, PartialEq, Eq)]
188pub struct SymbolTable {
189 pub symbols: BTreeMap<SymbolId, SymbolInfo>,
191
192 name_to_id: BTreeMap<Vec<String>, SymbolId>,
194
195 pub dependencies: BTreeMap<SymbolId, BTreeSet<SymbolId>>,
197}
198
199#[derive(Debug, Clone, PartialEq, Eq)]
200pub struct SymbolInfo {
201 pub id: SymbolId,
202 pub name: String,
203 pub path: Vec<String>,
204 pub kind: crate::SymbolKind,
205
206 pub resolved: bool,
208
209 pub dependencies: BTreeSet<SymbolId>,
211}
212
213impl SymbolTable {
214 pub fn new() -> Self {
215 Self {
216 symbols: BTreeMap::new(),
217 name_to_id: BTreeMap::new(),
218 dependencies: BTreeMap::new(),
219 }
220 }
221
222 pub fn register(&mut self, symbol: SymbolInfo) {
224 let id = symbol.id.clone();
225 let path = symbol.path.clone();
226
227 self.symbols.insert(id.clone(), symbol);
228 self.name_to_id.insert(path, id);
229 }
230
231 pub fn get(&self, id: &SymbolId) -> Option<&SymbolInfo> {
233 self.symbols.get(id)
234 }
235
236 pub fn get_by_path(&self, path: &[String]) -> Option<&SymbolInfo> {
238 self.name_to_id
239 .get(path)
240 .and_then(|id| self.symbols.get(id))
241 }
242
243 pub fn get_by_kind<'a>(
245 &'a self,
246 kind: &'a crate::SymbolKind,
247 ) -> impl Iterator<Item = &'a SymbolInfo> + 'a {
248 self.symbols.values().filter(move |info| &info.kind == kind)
249 }
250
251 pub fn add_dependency(&mut self, dependent: SymbolId, dependency: SymbolId) {
253 self.dependencies
254 .entry(dependent.clone())
255 .or_default()
256 .insert(dependency.clone());
257
258 if let Some(symbol) = self.symbols.get_mut(&dependent) {
260 symbol.dependencies.insert(dependency);
261 }
262 }
263
264 pub fn get_dependencies(&self, id: &SymbolId) -> Option<&BTreeSet<SymbolId>> {
266 self.dependencies.get(id)
267 }
268
269 pub fn topological_sort(&self) -> Result<Vec<SymbolId>, Vec<SymbolId>> {
271 let mut visited = BTreeSet::new();
272 let mut temp_visited = BTreeSet::new();
273 let mut result = Vec::new();
274
275 for id in self.symbols.keys() {
276 if !visited.contains(id) {
277 self.visit_topological(id, &mut visited, &mut temp_visited, &mut result)?;
278 }
279 }
280
281 Ok(result)
282 }
283
284 fn visit_topological(
285 &self,
286 id: &SymbolId,
287 visited: &mut BTreeSet<SymbolId>,
288 temp_visited: &mut BTreeSet<SymbolId>,
289 result: &mut Vec<SymbolId>,
290 ) -> Result<(), Vec<SymbolId>> {
291 if temp_visited.contains(id) {
292 return Err(vec![id.clone()]);
293 }
294
295 if visited.contains(id) {
296 return Ok(());
297 }
298
299 temp_visited.insert(id.clone());
300
301 if let Some(dependencies) = self.dependencies.get(id) {
302 for dep in dependencies {
303 self.visit_topological(dep, visited, temp_visited, result)?;
304 }
305 }
306
307 temp_visited.remove(id);
308 visited.insert(id.clone());
309 result.push(id.clone());
310
311 Ok(())
312 }
313}
314
315impl Default for SymbolTable {
316 fn default() -> Self {
317 Self::new()
318 }
319}
320
321impl SemanticSchema {
322 pub fn get_type_by_name(&self, name: &str) -> Option<&SemanticType> {
326 let path = name.split("::").map(|s| s.to_string()).collect::<Vec<_>>();
328 if let Some(info) = self.symbol_table.get_by_path(&path) {
329 if let Some(ty) = self.types.get(&info.id) {
330 return Some(ty);
331 }
332 }
333 if let Some(ty) = self.types.values().find(|t| t.name() == name) {
335 return Some(ty);
336 }
337 self.types.values().find(|t| t.original_name() == name)
339 }
340
341 pub fn get_type(&self, id: &SymbolId) -> Option<&SemanticType> {
343 self.types.get(id)
344 }
345
346 pub fn types(&self) -> impl Iterator<Item = &SemanticType> {
348 self.types.values()
349 }
350
351 pub fn functions(&self) -> impl Iterator<Item = &SemanticFunction> {
353 self.functions.values()
354 }
355
356 pub fn type_names(&self) -> impl Iterator<Item = &str> {
358 self.types.values().map(|t| t.name())
359 }
360}
361
362impl SemanticType {
363 pub fn id(&self) -> &SymbolId {
364 match self {
365 SemanticType::Primitive(p) => &p.id,
366 SemanticType::Struct(s) => &s.id,
367 SemanticType::Enum(e) => &e.id,
368 }
369 }
370
371 pub fn name(&self) -> &str {
372 match self {
373 SemanticType::Primitive(p) => &p.name,
374 SemanticType::Struct(s) => &s.name,
375 SemanticType::Enum(e) => &e.name,
376 }
377 }
378
379 pub fn original_name(&self) -> &str {
380 match self {
381 SemanticType::Primitive(p) => &p.original_name,
382 SemanticType::Struct(s) => &s.original_name,
383 SemanticType::Enum(e) => &e.original_name,
384 }
385 }
386}
387
388impl ResolvedTypeReference {
389 pub fn new(
391 target: SymbolId,
392 arguments: Vec<ResolvedTypeReference>,
393 original_name: String,
394 ) -> Self {
395 Self {
396 target,
397 arguments,
398 original_name,
399 }
400 }
401
402 pub fn is_primitive(&self, symbol_table: &SymbolTable) -> bool {
404 symbol_table
405 .get(&self.target)
406 .map(|info| matches!(info.kind, crate::SymbolKind::Primitive))
407 .unwrap_or(false)
408 }
409
410 pub fn is_generic(&self) -> bool {
412 !self.arguments.is_empty()
413 }
414}
415
416#[cfg(test)]
417mod tests {
418 use super::*;
419 use crate::{SymbolId, SymbolKind};
420
421 #[test]
422 fn test_symbol_table_basic_operations() {
423 let mut table = SymbolTable::new();
424
425 let user_id = SymbolId::struct_id(vec!["User".to_string()]);
426 let user_info = SymbolInfo {
427 id: user_id.clone(),
428 name: "User".to_string(),
429 path: vec!["User".to_string()],
430 kind: SymbolKind::Struct,
431 resolved: true,
432 dependencies: BTreeSet::new(),
433 };
434
435 table.register(user_info);
436
437 assert!(table.get(&user_id).is_some());
438 assert!(table.get_by_path(&["User".to_string()]).is_some());
439
440 let structs: Vec<_> = table.get_by_kind(&SymbolKind::Struct).collect();
441 assert_eq!(structs.len(), 1);
442 }
443
444 #[test]
445 fn test_symbol_table_dependencies() {
446 let mut table = SymbolTable::new();
447
448 let user_id = SymbolId::struct_id(vec!["User".to_string()]);
449 let post_id = SymbolId::struct_id(vec!["Post".to_string()]);
450
451 table.register(SymbolInfo {
452 id: user_id.clone(),
453 name: "User".to_string(),
454 path: vec!["User".to_string()],
455 kind: SymbolKind::Struct,
456 resolved: true,
457 dependencies: BTreeSet::new(),
458 });
459
460 table.register(SymbolInfo {
461 id: post_id.clone(),
462 name: "Post".to_string(),
463 path: vec!["Post".to_string()],
464 kind: SymbolKind::Struct,
465 resolved: true,
466 dependencies: BTreeSet::new(),
467 });
468
469 table.add_dependency(post_id.clone(), user_id.clone());
470
471 let deps = table.get_dependencies(&post_id).unwrap();
472 assert!(deps.contains(&user_id));
473
474 let sorted = table.topological_sort().unwrap();
475 let user_pos = sorted.iter().position(|id| id == &user_id).unwrap();
476 let post_pos = sorted.iter().position(|id| id == &post_id).unwrap();
477 assert!(
478 user_pos < post_pos,
479 "User should come before Post in topological order"
480 );
481 }
482
483 #[test]
484 fn test_resolved_type_reference() {
485 let string_id = SymbolId::new(SymbolKind::Primitive, vec!["String".to_string()]);
486 let vec_id = SymbolId::new(SymbolKind::Struct, vec!["Vec".to_string()]);
487
488 let string_ref =
489 ResolvedTypeReference::new(string_id.clone(), vec![], "String".to_string());
490
491 let vec_string_ref =
492 ResolvedTypeReference::new(vec_id, vec![string_ref], "Vec<String>".to_string());
493
494 assert!(!vec_string_ref.arguments.is_empty());
495 assert!(vec_string_ref.is_generic());
496 assert_eq!(vec_string_ref.arguments[0].target, string_id);
497 }
498}