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