Skip to main content

shape_ast/ast/
types.rs

1//! Type system definitions for Shape AST
2
3use super::DocComment;
4use super::functions::Annotation;
5use super::span::Span;
6use super::type_path::TypePath;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
10pub enum TypeAnnotation {
11    /// Basic types: number, string, bool, row, pattern, etc.
12    Basic(String),
13    /// Vector type: T[] or Vec<T>
14    Array(Box<TypeAnnotation>),
15    /// Tuple type: [T1, T2, T3]
16    Tuple(Vec<TypeAnnotation>),
17    /// Object type: { field1: T1, field2?: T2 }
18    Object(Vec<ObjectTypeField>),
19    /// Function type: (T1, T2) => T3
20    Function {
21        params: Vec<FunctionParam>,
22        returns: Box<TypeAnnotation>,
23    },
24    /// Union type: T1 | T2 | T3 (discriminated union - value is ONE of the types)
25    Union(Vec<TypeAnnotation>),
26    /// Intersection type: T1 + T2 (structural merge - value has ALL fields from both types)
27    /// Only valid for object/interface types. Field collisions are compile-time errors.
28    Intersection(Vec<TypeAnnotation>),
29    /// Generic type: Map<K, V>
30    Generic {
31        name: TypePath,
32        args: Vec<TypeAnnotation>,
33    },
34    /// Type reference (custom type or type alias)
35    Reference(TypePath),
36    /// Void type
37    Void,
38    /// Never type
39    Never,
40    /// Null type
41    Null,
42    /// Undefined type
43    Undefined,
44    /// Trait object type: dyn Trait1 + Trait2
45    /// Represents a type-erased value that implements the given traits
46    Dyn(Vec<TypePath>),
47}
48
49impl TypeAnnotation {
50    pub fn option(inner: TypeAnnotation) -> Self {
51        TypeAnnotation::Generic {
52            name: TypePath::simple("Option"),
53            args: vec![inner],
54        }
55    }
56
57    pub fn option_inner(&self) -> Option<&TypeAnnotation> {
58        match self {
59            TypeAnnotation::Generic { name, args }
60                if name.as_str() == "Option" && args.len() == 1 =>
61            {
62                args.first()
63            }
64            _ => None,
65        }
66    }
67
68    pub fn into_option_inner(self) -> Option<TypeAnnotation> {
69        match self {
70            TypeAnnotation::Generic { name, mut args }
71                if name.as_str() == "Option" && args.len() == 1 =>
72            {
73                Some(args.remove(0))
74            }
75            _ => None,
76        }
77    }
78
79    pub fn is_option(&self) -> bool {
80        self.option_inner().is_some()
81    }
82
83    /// Extract a simple type name if this is a Reference or Basic type
84    ///
85    /// Returns `Some(type_name)` for:
86    /// - `TypeAnnotation::Reference(path)` - e.g., `Currency`, `foo::MyType`
87    /// - `TypeAnnotation::Basic(name)` - e.g., `number`, `string`
88    ///
89    /// Returns `None` for complex types like arrays, tuples, functions, etc.
90    pub fn as_simple_name(&self) -> Option<&str> {
91        match self {
92            TypeAnnotation::Reference(path) => Some(path.as_str()),
93            TypeAnnotation::Basic(name) => Some(name.as_str()),
94            _ => None,
95        }
96    }
97
98    /// Extract the type name string for Basic or Reference variants.
99    /// Handles the `Basic(name) | Reference(path)` pattern uniformly.
100    pub fn as_type_name_str(&self) -> Option<&str> {
101        match self {
102            TypeAnnotation::Basic(name) => Some(name.as_str()),
103            TypeAnnotation::Reference(path) => Some(path.as_str()),
104            _ => None,
105        }
106    }
107
108    /// Convert a type annotation to its full string representation.
109    pub fn to_type_string(&self) -> String {
110        match self {
111            TypeAnnotation::Basic(name) => name.clone(),
112            TypeAnnotation::Reference(path) => path.to_string(),
113            TypeAnnotation::Array(inner) => format!("Array<{}>", inner.to_type_string()),
114            TypeAnnotation::Generic { name, args }
115                if name.as_str() == "Option" && args.len() == 1 =>
116            {
117                format!("{}?", args[0].to_type_string())
118            }
119            TypeAnnotation::Generic { name, args } => {
120                let args_str: Vec<String> = args.iter().map(|a| a.to_type_string()).collect();
121                format!("{}<{}>", name, args_str.join(", "))
122            }
123            TypeAnnotation::Tuple(items) => {
124                let items_str: Vec<String> = items.iter().map(|t| t.to_type_string()).collect();
125                format!("[{}]", items_str.join(", "))
126            }
127            TypeAnnotation::Union(items) => {
128                let items_str: Vec<String> = items.iter().map(|t| t.to_type_string()).collect();
129                items_str.join(" | ")
130            }
131            TypeAnnotation::Void => "void".to_string(),
132            TypeAnnotation::Never => "never".to_string(),
133            TypeAnnotation::Null => "null".to_string(),
134            TypeAnnotation::Undefined => "undefined".to_string(),
135            TypeAnnotation::Object(fields) => {
136                let fields_str: Vec<String> = fields
137                    .iter()
138                    .map(|f| {
139                        let opt = if f.optional { "?" } else { "" };
140                        // Include @alias in the type string so the Python extension
141                        // can use the wire name when looking up dict keys.
142                        let alias = f
143                            .annotations
144                            .iter()
145                            .find(|a| a.name == "alias")
146                            .and_then(|a| a.args.first())
147                            .and_then(|arg| match arg {
148                                super::expressions::Expr::Literal(
149                                    super::literals::Literal::String(s),
150                                    _,
151                                ) => Some(s.as_str()),
152                                _ => None,
153                            });
154                        let alias_str = alias.map(|a| format!("@\"{}\" ", a)).unwrap_or_default();
155                        format!(
156                            "{}{}{}: {}",
157                            alias_str,
158                            f.name,
159                            opt,
160                            f.type_annotation.to_type_string()
161                        )
162                    })
163                    .collect();
164                format!("{{{}}}", fields_str.join(", "))
165            }
166            _ => "any".to_string(),
167        }
168    }
169}
170
171#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
172pub struct ObjectTypeField {
173    pub name: String,
174    pub optional: bool,
175    pub type_annotation: TypeAnnotation,
176    /// Field annotations (e.g. `@alias("wire name")`)
177    #[serde(default)]
178    pub annotations: Vec<Annotation>,
179}
180
181#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
182pub struct FunctionParam {
183    pub name: Option<String>,
184    pub optional: bool,
185    pub type_annotation: TypeAnnotation,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct TypeParam {
190    pub name: String,
191    #[serde(default)]
192    pub span: Span,
193    #[serde(default)]
194    pub doc_comment: Option<DocComment>,
195    /// Default type argument: `T = int`
196    pub default_type: Option<TypeAnnotation>,
197    /// Trait bounds: `T: Comparable + Displayable`
198    #[serde(default)]
199    pub trait_bounds: Vec<TypePath>,
200}
201
202/// A predicate in a where clause: `T: Comparable + Display`
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
204pub struct WherePredicate {
205    pub type_name: String,
206    pub bounds: Vec<TypePath>,
207}
208
209impl PartialEq for TypeParam {
210    fn eq(&self, other: &Self) -> bool {
211        self.name == other.name
212            && self.doc_comment == other.doc_comment
213            && self.default_type == other.default_type
214            && self.trait_bounds == other.trait_bounds
215    }
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct TypeAliasDef {
220    pub name: String,
221    #[serde(default)]
222    pub doc_comment: Option<DocComment>,
223    pub type_params: Option<Vec<TypeParam>>,
224    pub type_annotation: TypeAnnotation,
225    /// Meta parameter overrides: type Percent4 = Percent { decimals: 4 }
226    pub meta_param_overrides: Option<std::collections::HashMap<String, super::expressions::Expr>>,
227}
228
229#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct InterfaceDef {
231    pub name: String,
232    #[serde(default)]
233    pub doc_comment: Option<DocComment>,
234    pub type_params: Option<Vec<TypeParam>>,
235    pub members: Vec<InterfaceMember>,
236}
237
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub enum InterfaceMember {
240    /// Property signature
241    Property {
242        name: String,
243        optional: bool,
244        type_annotation: TypeAnnotation,
245        #[serde(default)]
246        span: Span,
247        #[serde(default)]
248        doc_comment: Option<DocComment>,
249    },
250    /// Method signature
251    Method {
252        name: String,
253        optional: bool,
254        params: Vec<FunctionParam>,
255        return_type: TypeAnnotation,
256        /// Whether this is an async method
257        is_async: bool,
258        #[serde(default)]
259        span: Span,
260        #[serde(default)]
261        doc_comment: Option<DocComment>,
262    },
263    /// Index signature
264    IndexSignature {
265        param_name: String,
266        param_type: String, // "string" or "number"
267        return_type: TypeAnnotation,
268        #[serde(default)]
269        span: Span,
270        #[serde(default)]
271        doc_comment: Option<DocComment>,
272    },
273}
274
275impl InterfaceMember {
276    pub fn span(&self) -> Span {
277        match self {
278            InterfaceMember::Property { span, .. }
279            | InterfaceMember::Method { span, .. }
280            | InterfaceMember::IndexSignature { span, .. } => *span,
281        }
282    }
283
284    pub fn doc_comment(&self) -> Option<&DocComment> {
285        match self {
286            InterfaceMember::Property { doc_comment, .. }
287            | InterfaceMember::Method { doc_comment, .. }
288            | InterfaceMember::IndexSignature { doc_comment, .. } => doc_comment.as_ref(),
289        }
290    }
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
294pub struct EnumDef {
295    pub name: String,
296    #[serde(default)]
297    pub doc_comment: Option<DocComment>,
298    pub type_params: Option<Vec<TypeParam>>,
299    pub members: Vec<EnumMember>,
300    /// Annotations applied to the enum (e.g., `@with_label() enum Color { ... }`)
301    #[serde(default)]
302    pub annotations: Vec<super::Annotation>,
303}
304
305#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct EnumMember {
307    pub name: String,
308    pub kind: EnumMemberKind,
309    #[serde(default)]
310    pub span: Span,
311    #[serde(default)]
312    pub doc_comment: Option<DocComment>,
313}
314
315#[derive(Debug, Clone, Serialize, Deserialize)]
316pub enum EnumMemberKind {
317    /// Unit variant: Variant or Variant = 1
318    Unit { value: Option<EnumValue> },
319    /// Tuple variant: Variant(Type, Type)
320    Tuple(Vec<TypeAnnotation>),
321    /// Struct variant: Variant { field: Type, ... }
322    Struct(Vec<ObjectTypeField>),
323}
324
325#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
326pub enum EnumValue {
327    String(String),
328    Number(f64),
329}
330
331/// A member of a trait definition: either required (signature only) or default (with body)
332#[derive(Debug, Clone, Serialize, Deserialize)]
333pub enum TraitMember {
334    /// Required method — implementors must provide this
335    Required(InterfaceMember),
336    /// Default method — used if implementor does not override
337    Default(MethodDef),
338    /// Associated type declaration: `type Item;` or `type Item: Comparable;`
339    AssociatedType {
340        name: String,
341        bounds: Vec<TypeAnnotation>,
342        #[serde(default)]
343        span: Span,
344        #[serde(default)]
345        doc_comment: Option<DocComment>,
346    },
347}
348
349impl TraitMember {
350    pub fn span(&self) -> Span {
351        match self {
352            TraitMember::Required(member) => member.span(),
353            TraitMember::Default(method) => method.span,
354            TraitMember::AssociatedType { span, .. } => *span,
355        }
356    }
357
358    pub fn doc_comment(&self) -> Option<&DocComment> {
359        match self {
360            TraitMember::Required(member) => member.doc_comment(),
361            TraitMember::Default(method) => method.doc_comment.as_ref(),
362            TraitMember::AssociatedType { doc_comment, .. } => doc_comment.as_ref(),
363        }
364    }
365}
366
367/// A concrete binding for an associated type inside an `impl` block:
368/// `type Item = number;`
369#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct AssociatedTypeBinding {
371    pub name: String,
372    pub concrete_type: TypeAnnotation,
373}
374
375/// Trait definition — like interface but with `trait` keyword, supporting default methods
376///
377/// ```shape
378/// trait Queryable<T> {
379///     filter(predicate: (T) => bool): Self    // required
380///     method execute() -> Result<Table<T>> {   // default
381///         return Ok(self.filter(|_| true))
382///     }
383/// }
384/// ```
385#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct TraitDef {
387    pub name: String,
388    #[serde(default)]
389    pub doc_comment: Option<DocComment>,
390    pub type_params: Option<Vec<TypeParam>>,
391    /// Supertrait bounds: `trait Foo: Bar + Baz { ... }`
392    #[serde(default)]
393    pub super_traits: Vec<TypeAnnotation>,
394    pub members: Vec<TraitMember>,
395    /// Annotations applied to the trait (e.g., `@documented("...") trait Foo { ... }`)
396    #[serde(default)]
397    pub annotations: Vec<super::Annotation>,
398}
399
400/// Impl block — implements a trait for a type
401///
402/// ```shape
403/// impl Queryable<T> for Table<T> {
404///     method filter(predicate) { /* ... */ }
405///     method execute() { Ok(self) }
406/// }
407/// ```
408///
409/// Under the hood, compiles identically to an extend block (UFCS desugaring)
410/// plus trait validation (all required methods present with correct arities).
411#[derive(Debug, Clone, Serialize, Deserialize)]
412pub struct ImplBlock {
413    /// The trait being implemented (e.g., Queryable<T>)
414    pub trait_name: TypeName,
415    /// The type implementing the trait (e.g., Table<T>)
416    pub target_type: TypeName,
417    /// Optional named implementation selector:
418    /// `impl Display for User as JsonDisplay { ... }`
419    pub impl_name: Option<String>,
420    /// Method implementations
421    pub methods: Vec<MethodDef>,
422    /// Associated type bindings: `type Item = number;`
423    pub associated_type_bindings: Vec<AssociatedTypeBinding>,
424    /// Where clause: `where T: Display + Comparable`
425    pub where_clause: Option<Vec<WherePredicate>>,
426}
427
428/// Type extension for adding methods to existing types
429#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
430pub struct ExtendStatement {
431    /// The type being extended (e.g., "Vec")
432    pub type_name: TypeName,
433    /// Methods being added to the type
434    pub methods: Vec<MethodDef>,
435}
436
437#[derive(Debug, Clone, Serialize, Deserialize)]
438pub struct MethodDef {
439    /// Method name
440    pub name: String,
441    #[serde(default)]
442    pub span: Span,
443    /// Declaring module path for compiler/runtime provenance checks.
444    ///
445    /// This is injected by the module loader for loaded modules and is not part
446    /// of user-authored source syntax.
447    #[serde(default, skip_serializing_if = "Option::is_none")]
448    pub declaring_module_path: Option<String>,
449    #[serde(default)]
450    pub doc_comment: Option<DocComment>,
451    /// Annotations applied to this method (e.g., `@traced`)
452    #[serde(default)]
453    pub annotations: Vec<super::functions::Annotation>,
454    /// Type parameters for generic methods (e.g., `method map<U>(...)`)
455    #[serde(default)]
456    pub type_params: Option<Vec<TypeParam>>,
457    /// Method parameters
458    pub params: Vec<super::functions::FunctionParameter>,
459    /// Optional when clause for conditional method definitions
460    pub when_clause: Option<Box<super::expressions::Expr>>,
461    /// Optional return type annotation
462    pub return_type: Option<TypeAnnotation>,
463    /// Method body
464    pub body: Vec<super::statements::Statement>,
465    /// Whether this is an async method
466    pub is_async: bool,
467}
468
469impl PartialEq for MethodDef {
470    fn eq(&self, other: &Self) -> bool {
471        self.name == other.name
472            && self.doc_comment == other.doc_comment
473            && self.annotations == other.annotations
474            && self.type_params == other.type_params
475            && self.params == other.params
476            && self.when_clause == other.when_clause
477            && self.return_type == other.return_type
478            && self.body == other.body
479            && self.is_async == other.is_async
480    }
481}
482
483#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
484pub enum TypeName {
485    /// Simple type name (e.g., "Vec", "Table", "foo::Bar")
486    Simple(TypePath),
487    /// Generic type name (e.g., "Table<Row>")
488    Generic {
489        name: TypePath,
490        type_args: Vec<TypeAnnotation>,
491    },
492}
493
494// ============================================================================
495// Struct Type Definitions
496// ============================================================================
497
498/// Struct type definition — pure data with named fields
499///
500/// ```shape
501/// type Point { x: number, y: number }
502/// type DataVec<V, K = Timestamp> { index: Vec<K>, data: Vec<V> }
503/// ```
504#[derive(Debug, Clone, Serialize, Deserialize)]
505pub struct StructTypeDef {
506    pub name: String,
507    #[serde(default)]
508    pub doc_comment: Option<DocComment>,
509    pub type_params: Option<Vec<TypeParam>>,
510    pub fields: Vec<StructField>,
511    /// Inline method definitions inside the type body
512    #[serde(default)]
513    pub methods: Vec<MethodDef>,
514    /// Annotations applied to the struct (e.g., `@derive_debug type Foo { ... }`)
515    pub annotations: Vec<Annotation>,
516    /// Optional native layout metadata for `type C`.
517    #[serde(default)]
518    pub native_layout: Option<NativeLayoutBinding>,
519}
520
521/// Native layout binding metadata for `type C` declarations.
522#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
523pub struct NativeLayoutBinding {
524    /// ABI name (currently `"C"`).
525    pub abi: String,
526}
527
528/// A field in a struct type definition
529///
530/// Comptime fields are type-level constants baked at compile time.
531/// They occupy zero runtime slots (no ValueSlot in TypedObject).
532/// Access resolves to a constant push at compile time.
533///
534/// ```shape
535/// type Currency {
536///     comptime symbol: string = "$",
537///     comptime decimals: number = 2,
538///     amount: number,
539/// }
540/// ```
541#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct StructField {
543    pub annotations: Vec<Annotation>,
544    pub is_comptime: bool,
545    pub name: String,
546    #[serde(default)]
547    pub span: Span,
548    #[serde(default)]
549    pub doc_comment: Option<DocComment>,
550    pub type_annotation: TypeAnnotation,
551    pub default_value: Option<super::expressions::Expr>,
552}
553
554impl PartialEq for StructField {
555    fn eq(&self, other: &Self) -> bool {
556        self.annotations == other.annotations
557            && self.is_comptime == other.is_comptime
558            && self.name == other.name
559            && self.doc_comment == other.doc_comment
560            && self.type_annotation == other.type_annotation
561            && self.default_value == other.default_value
562    }
563}