ferro_type/
lib.rs

1//! Ferrotype: Rust-to-TypeScript type generation via derive macro
2//!
3//! This crate provides traits and derive macros for generating TypeScript type
4//! definitions from Rust structs and enums using an intermediate representation
5//! for deduplication and dependency ordering.
6//!
7//! # Core Design
8//!
9//! The core abstraction is the [`TypeScript`] trait, modeled after serde's `Serialize`.
10//! Rather than returning strings directly, it returns a [`TypeDef`] intermediate
11//! representation that can be:
12//! - Rendered to TypeScript syntax
13//! - Analyzed for type dependencies
14//! - Deduplicated for cleaner output
15//! - Extended for additional targets
16
17pub use ferro_type_derive::TypeScript;
18pub use linkme;
19
20use std::collections::HashMap;
21
22// ============================================================================
23// AUTO-REGISTRATION VIA DISTRIBUTED SLICE
24// ============================================================================
25
26/// Distributed slice for auto-registration of TypeScript types.
27///
28/// Types that derive `TypeScript` are automatically registered in this slice
29/// when the `auto_register` feature is enabled (default). This allows collecting
30/// all types without manual registration.
31///
32/// # Usage
33///
34/// ```ignore
35/// // Types are automatically registered when you derive TypeScript
36/// #[derive(TypeScript)]
37/// struct User { name: String }
38///
39/// // Collect all registered types
40/// let registry = TypeRegistry::from_distributed();
41/// ```
42#[linkme::distributed_slice]
43pub static TYPESCRIPT_TYPES: [fn() -> TypeDef];
44
45// ============================================================================
46// CORE TRAIT AND IR (TypeScript + TypeDef)
47// ============================================================================
48
49/// The core trait for types that can be represented as TypeScript.
50///
51/// This is the foundation trait for ferrotype, similar to how `Serialize` is
52/// the foundation of serde. Implementations return a [`TypeDef`] that describes
53/// the TypeScript representation of the type.
54///
55/// # Design Philosophy
56///
57/// Unlike string-based approaches, returning a structured [`TypeDef`] enables:
58/// - **Deduplication**: Named types can be collected and emitted once
59/// - **Analysis**: Dependencies between types can be tracked
60/// - **Flexibility**: The IR can be rendered in different styles
61/// - **Composition**: Complex types build from simpler TypeDefs
62///
63/// # Example
64///
65/// ```ignore
66/// use ferrotype::{TypeScript, TypeDef, Primitive};
67///
68/// struct UserId(String);
69///
70/// impl TypeScript for UserId {
71///     fn typescript() -> TypeDef {
72///         TypeDef::Named {
73///             namespace: vec![],
74///             name: "UserId".into(),
75///             def: Box::new(TypeDef::Primitive(Primitive::String)),
76///         }
77///     }
78/// }
79/// ```
80pub trait TypeScript {
81    /// Returns the TypeScript type definition for this type.
82    fn typescript() -> TypeDef;
83}
84
85/// Intermediate representation for TypeScript types.
86///
87/// This enum represents all TypeScript types that ferrotype can generate.
88/// It serves as the IR between Rust types and TypeScript output, enabling
89/// analysis and transformation before rendering.
90///
91/// # Type Categories
92///
93/// - **Primitives**: `string`, `number`, `boolean`, `null`, etc.
94/// - **Compounds**: Arrays, tuples, objects, unions, intersections
95/// - **References**: Named types and type references
96/// - **Literals**: Specific string, number, or boolean values
97#[derive(Debug, Clone, PartialEq)]
98pub enum TypeDef {
99    /// A primitive TypeScript type.
100    Primitive(Primitive),
101
102    /// An array type: `T[]`
103    Array(Box<TypeDef>),
104
105    /// A tuple type: `[T1, T2, ...]`
106    Tuple(Vec<TypeDef>),
107
108    /// An object type with named fields: `{ field1: T1; field2?: T2; }`
109    Object(Vec<Field>),
110
111    /// A union type: `T1 | T2 | ...`
112    Union(Vec<TypeDef>),
113
114    /// An intersection type: `T1 & T2 & ...`
115    Intersection(Vec<TypeDef>),
116
117    /// A record/dictionary type: `Record<K, V>` or `{ [key: K]: V }`
118    Record {
119        key: Box<TypeDef>,
120        value: Box<TypeDef>,
121    },
122
123    /// A named type definition that should be emitted as a separate declaration.
124    /// This is the primary mechanism for type deduplication.
125    ///
126    /// The optional `namespace` field allows placing types in TypeScript namespaces:
127    /// - `namespace: vec![]` → `type State = ...`
128    /// - `namespace: vec!["VM".into(), "Git".into()]` → `namespace VM { namespace Git { type State = ... } }`
129    Named {
130        /// Optional namespace path, e.g., ["VM", "Git"] for `namespace VM { namespace Git { ... } }`
131        namespace: Vec<String>,
132        /// The type name
133        name: String,
134        /// The type definition
135        def: Box<TypeDef>,
136        /// Optional module path for multi-file export (e.g., "models::user")
137        module: Option<String>,
138    },
139
140    /// A reference to a named type. Used to avoid infinite recursion and
141    /// to generate cleaner output by referencing previously-defined types.
142    Ref(String),
143
144    /// A literal type with a specific value.
145    Literal(Literal),
146
147    /// A function type: `(arg1: T1, arg2: T2) => R`
148    Function {
149        params: Vec<Field>,
150        return_type: Box<TypeDef>,
151    },
152
153    /// A generic type application: `Generic<T1, T2>`
154    Generic {
155        base: String,
156        args: Vec<TypeDef>,
157    },
158
159    /// An indexed access type: `T["K"]`
160    ///
161    /// Indexed access types allow extracting a property type from another type.
162    /// This is commonly used for type-safe property access patterns.
163    ///
164    /// # Example
165    ///
166    /// ```ignore
167    /// // Profile["login"] extracts the type of the "login" property from Profile
168    /// let login_type = TypeDef::IndexedAccess {
169    ///     base: "Profile".into(),
170    ///     key: "login".into(),
171    /// };
172    /// assert_eq!(login_type.render(), "Profile[\"login\"]");
173    /// ```
174    IndexedAccess {
175        /// The base type to index into (e.g., "Profile")
176        base: String,
177        /// The property key to access (e.g., "login")
178        key: String,
179    },
180
181    /// A template literal type: `` `prefix${Type}suffix` ``
182    ///
183    /// Template literal types enable compile-time string pattern validation in TypeScript.
184    /// They're commonly used for branded IDs like `vm-${string}` or version patterns.
185    ///
186    /// # Example
187    ///
188    /// ```ignore
189    /// // `vm-${string}` validates that strings start with "vm-"
190    /// let vm_id = TypeDef::TemplateLiteral {
191    ///     strings: vec!["vm-".into(), "".into()],
192    ///     types: vec![Box::new(TypeDef::Primitive(Primitive::String))],
193    /// };
194    /// assert_eq!(vm_id.render(), "`vm-${string}`");
195    /// ```
196    TemplateLiteral {
197        /// String parts interspersed with type placeholders.
198        /// Always has one more element than `types`.
199        /// e.g., "vm-" + ${string} + "" becomes ["vm-", ""]
200        strings: Vec<String>,
201        /// Types to interpolate (one fewer than strings).
202        types: Vec<Box<TypeDef>>,
203    },
204
205    /// A generic type definition with type parameters.
206    ///
207    /// This is for **defining** a generic type with parameters.
208    /// For example: `type Core<T extends { type: string }> = { id: string; data: T }`
209    ///
210    /// # The Core<T> Pattern
211    ///
212    /// A common pattern for rich discriminated unions wraps variants in a generic:
213    ///
214    /// ```typescript
215    /// // Generic wrapper for common metadata
216    /// interface Core<T extends { type: string }> {
217    ///     id: string;
218    ///     timestamp: Date;
219    ///     data: T;
220    /// }
221    ///
222    /// // Variant types
223    /// interface TextData { type: "text"; content: string }
224    /// interface ImageData { type: "image"; url: string }
225    ///
226    /// // Full message types
227    /// type TextMessage = Core<TextData>;
228    /// type ImageMessage = Core<ImageData>;
229    /// type Message = TextMessage | ImageMessage;
230    /// ```
231    ///
232    /// # Example
233    ///
234    /// ```ignore
235    /// use ferrotype::{TypeDef, TypeParam, Field, Primitive};
236    ///
237    /// // Define: type Core<T extends { type: string }> = { id: string; data: T }
238    /// let core_def = TypeDef::GenericDef {
239    ///     name: "Core".into(),
240    ///     type_params: vec![
241    ///         TypeParam::new("T").with_constraint(TypeDef::Object(vec![
242    ///             Field::new("type", TypeDef::Primitive(Primitive::String)),
243    ///         ])),
244    ///     ],
245    ///     def: Box::new(TypeDef::Object(vec![
246    ///         Field::new("id", TypeDef::Primitive(Primitive::String)),
247    ///         Field::new("data", TypeDef::TypeParamRef("T".into())),
248    ///     ])),
249    /// };
250    /// ```
251    GenericDef {
252        /// The name of the generic type
253        name: String,
254        /// Type parameters with optional constraints and defaults
255        type_params: Vec<TypeParam>,
256        /// The type definition body (may reference type params via TypeParamRef)
257        def: Box<TypeDef>,
258    },
259
260    /// A reference to a type parameter within a generic definition.
261    ///
262    /// This is used inside a `GenericDef` to reference one of its type parameters.
263    /// For example, in `type Core<T> = { data: T }`, the `T` in `data: T` is a `TypeParamRef`.
264    TypeParamRef(String),
265}
266
267/// Primitive TypeScript types.
268#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
269pub enum Primitive {
270    /// The `string` type.
271    String,
272    /// The `number` type.
273    Number,
274    /// The `boolean` type.
275    Boolean,
276    /// The `null` type.
277    Null,
278    /// The `undefined` type.
279    Undefined,
280    /// The `void` type (for functions that don't return a value).
281    Void,
282    /// The `never` type (for functions that never return).
283    Never,
284    /// The `any` type (escape hatch, use sparingly).
285    Any,
286    /// The `unknown` type (type-safe alternative to any).
287    Unknown,
288    /// The `bigint` type.
289    BigInt,
290}
291
292/// A field in an object type.
293#[derive(Debug, Clone, PartialEq)]
294pub struct Field {
295    /// The field name.
296    pub name: String,
297    /// The field's type.
298    pub ty: TypeDef,
299    /// Whether the field is optional (`field?: T` vs `field: T`).
300    pub optional: bool,
301    /// Whether the field is readonly.
302    pub readonly: bool,
303}
304
305/// A type parameter for generic type definitions.
306///
307/// Represents a type parameter like `T`, `T extends string`, or `T = never`.
308///
309/// # Examples
310///
311/// ```ignore
312/// // Simple type parameter: T
313/// TypeParam::new("T")
314///
315/// // Constrained type parameter: T extends { type: string }
316/// TypeParam::new("T").with_constraint(TypeDef::Object(...))
317///
318/// // Type parameter with default: T = never
319/// TypeParam::new("T").with_default(TypeDef::Primitive(Primitive::Never))
320/// ```
321#[derive(Debug, Clone, PartialEq)]
322pub struct TypeParam {
323    /// The type parameter name, e.g., "T", "K", "V"
324    pub name: String,
325    /// Optional constraint: `T extends Constraint`
326    pub constraint: Option<Box<TypeDef>>,
327    /// Optional default value: `T = Default`
328    pub default: Option<Box<TypeDef>>,
329}
330
331impl TypeParam {
332    /// Creates a new unconstrained type parameter.
333    pub fn new(name: impl Into<String>) -> Self {
334        Self {
335            name: name.into(),
336            constraint: None,
337            default: None,
338        }
339    }
340
341    /// Adds a constraint to this type parameter: `T extends Constraint`
342    pub fn with_constraint(mut self, constraint: TypeDef) -> Self {
343        self.constraint = Some(Box::new(constraint));
344        self
345    }
346
347    /// Adds a default type to this type parameter: `T = Default`
348    pub fn with_default(mut self, default: TypeDef) -> Self {
349        self.default = Some(Box::new(default));
350        self
351    }
352
353    /// Renders this type parameter to TypeScript syntax.
354    ///
355    /// # Examples
356    ///
357    /// - `T` → `"T"`
358    /// - `T extends string` → `"T extends string"`
359    /// - `T = never` → `"T = never"`
360    /// - `T extends string = "default"` → `"T extends string = \"default\""`
361    pub fn render(&self) -> String {
362        let mut result = self.name.clone();
363
364        if let Some(ref constraint) = self.constraint {
365            result.push_str(" extends ");
366            result.push_str(&constraint.render());
367        }
368
369        if let Some(ref default) = self.default {
370            result.push_str(" = ");
371            result.push_str(&default.render());
372        }
373
374        result
375    }
376}
377
378impl Field {
379    /// Creates a new required field.
380    pub fn new(name: impl Into<String>, ty: TypeDef) -> Self {
381        Self {
382            name: name.into(),
383            ty,
384            optional: false,
385            readonly: false,
386        }
387    }
388
389    /// Creates a new optional field.
390    pub fn optional(name: impl Into<String>, ty: TypeDef) -> Self {
391        Self {
392            name: name.into(),
393            ty,
394            optional: true,
395            readonly: false,
396        }
397    }
398
399    /// Makes this field readonly.
400    pub fn readonly(mut self) -> Self {
401        self.readonly = true;
402        self
403    }
404}
405
406/// A literal TypeScript type with a specific value.
407#[derive(Debug, Clone, PartialEq)]
408pub enum Literal {
409    /// A string literal: `"foo"`
410    String(String),
411    /// A number literal: `42`
412    Number(f64),
413    /// A boolean literal: `true` or `false`
414    Boolean(bool),
415}
416
417impl TypeDef {
418    /// Renders this TypeDef to TypeScript syntax.
419    pub fn render(&self) -> String {
420        match self {
421            TypeDef::Primitive(p) => p.render().to_string(),
422            TypeDef::Array(inner) => {
423                let inner_str = inner.render();
424                // Wrap union types in parens for array syntax
425                if matches!(inner.as_ref(), TypeDef::Union(_)) {
426                    format!("({})[]", inner_str)
427                } else {
428                    format!("{}[]", inner_str)
429                }
430            }
431            TypeDef::Tuple(items) => {
432                let items_str: Vec<_> = items.iter().map(|t| t.render()).collect();
433                format!("[{}]", items_str.join(", "))
434            }
435            TypeDef::Object(fields) => {
436                if fields.is_empty() {
437                    "{}".to_string()
438                } else {
439                    let fields_str: Vec<_> = fields
440                        .iter()
441                        .map(|f| {
442                            let readonly = if f.readonly { "readonly " } else { "" };
443                            let opt = if f.optional { "?" } else { "" };
444                            format!("{}{}{}: {}", readonly, f.name, opt, f.ty.render())
445                        })
446                        .collect();
447                    format!("{{ {} }}", fields_str.join("; "))
448                }
449            }
450            TypeDef::Union(variants) => {
451                let variants_str: Vec<_> = variants.iter().map(|t| t.render()).collect();
452                variants_str.join(" | ")
453            }
454            TypeDef::Intersection(types) => {
455                let types_str: Vec<_> = types.iter().map(|t| t.render()).collect();
456                types_str.join(" & ")
457            }
458            TypeDef::Record { key, value } => {
459                format!("Record<{}, {}>", key.render(), value.render())
460            }
461            TypeDef::Named { namespace, name, .. } => {
462                if namespace.is_empty() {
463                    name.clone()
464                } else {
465                    format!("{}.{}", namespace.join("."), name)
466                }
467            }
468            TypeDef::Ref(name) => name.clone(),
469            TypeDef::Literal(lit) => lit.render(),
470            TypeDef::Function {
471                params,
472                return_type,
473            } => {
474                let params_str: Vec<_> = params
475                    .iter()
476                    .map(|p| format!("{}: {}", p.name, p.ty.render()))
477                    .collect();
478                format!("({}) => {}", params_str.join(", "), return_type.render())
479            }
480            TypeDef::Generic { base, args } => {
481                let args_str: Vec<_> = args.iter().map(|t| t.render()).collect();
482                format!("{}<{}>", base, args_str.join(", "))
483            }
484            TypeDef::IndexedAccess { base, key } => {
485                format!("{}[\"{}\"]", base, key.replace('\\', "\\\\").replace('"', "\\\""))
486            }
487            TypeDef::TemplateLiteral { strings, types } => {
488                let mut result = String::from("`");
489                for (i, s) in strings.iter().enumerate() {
490                    // Escape backticks and backslashes in template literal strings
491                    let escaped = s.replace('\\', "\\\\").replace('`', "\\`");
492                    result.push_str(&escaped);
493                    if i < types.len() {
494                        result.push_str("${");
495                        result.push_str(&types[i].render());
496                        result.push('}');
497                    }
498                }
499                result.push('`');
500                result
501            }
502            TypeDef::GenericDef { name, .. } => {
503                // When used inline, a generic definition renders as just its name
504                // (like Named types). Use render_declaration() for the full definition.
505                name.clone()
506            }
507            TypeDef::TypeParamRef(name) => {
508                // Type parameter references render as just the parameter name
509                name.clone()
510            }
511        }
512    }
513
514    /// Renders a full type declaration for named types.
515    ///
516    /// For `Named` types, this returns `type Name = Definition;`
517    /// For `GenericDef` types, this returns `type Name<T, ...> = Definition;`
518    /// If the type has a namespace, it wraps in `namespace X { ... }`.
519    /// For other types, this just returns the rendered type.
520    pub fn render_declaration(&self) -> String {
521        match self {
522            TypeDef::Named { namespace, name, def, .. } => {
523                let inner = format!("type {} = {};", name, def.render());
524                Self::wrap_in_namespace(namespace, &inner)
525            }
526            TypeDef::GenericDef {
527                name,
528                type_params,
529                def,
530            } => {
531                let params_str: Vec<_> = type_params.iter().map(|p| p.render()).collect();
532                format!("type {}<{}> = {};", name, params_str.join(", "), def.render())
533            }
534            _ => self.render(),
535        }
536    }
537
538    /// Wraps a declaration in namespace blocks.
539    ///
540    /// For namespace `["VM", "Git"]` and inner `type State = "clean";`:
541    /// ```typescript
542    /// namespace VM {
543    ///     namespace Git {
544    ///         type State = "clean";
545    ///     }
546    /// }
547    /// ```
548    fn wrap_in_namespace(namespace: &[String], inner: &str) -> String {
549        if namespace.is_empty() {
550            return inner.to_string();
551        }
552
553        let mut result = String::new();
554        let indent = "    ";
555
556        // Opening namespace declarations
557        for (i, ns) in namespace.iter().enumerate() {
558            for _ in 0..i {
559                result.push_str(indent);
560            }
561            result.push_str("namespace ");
562            result.push_str(ns);
563            result.push_str(" {\n");
564        }
565
566        // Inner content with proper indentation
567        let depth = namespace.len();
568        for _ in 0..depth {
569            result.push_str(indent);
570        }
571        result.push_str(inner);
572        result.push('\n');
573
574        // Closing braces
575        for i in (0..depth).rev() {
576            for _ in 0..i {
577                result.push_str(indent);
578            }
579            result.push('}');
580            if i > 0 {
581                result.push('\n');
582            }
583        }
584
585        result
586    }
587}
588
589impl Primitive {
590    /// Renders this primitive to its TypeScript keyword.
591    pub fn render(&self) -> &'static str {
592        match self {
593            Primitive::String => "string",
594            Primitive::Number => "number",
595            Primitive::Boolean => "boolean",
596            Primitive::Null => "null",
597            Primitive::Undefined => "undefined",
598            Primitive::Void => "void",
599            Primitive::Never => "never",
600            Primitive::Any => "any",
601            Primitive::Unknown => "unknown",
602            Primitive::BigInt => "bigint",
603        }
604    }
605}
606
607impl Literal {
608    /// Renders this literal to TypeScript syntax.
609    pub fn render(&self) -> String {
610        match self {
611            Literal::String(s) => format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")),
612            Literal::Number(n) => {
613                if n.fract() == 0.0 {
614                    format!("{}", *n as i64)
615                } else {
616                    format!("{}", n)
617                }
618            }
619            Literal::Boolean(b) => b.to_string(),
620        }
621    }
622}
623
624// ============================================================================
625// HELPER FUNCTIONS
626// ============================================================================
627
628/// Extracts fields from an Object TypeDef, unwrapping Named if necessary.
629///
630/// This is used by the derive macro to implement `#[ts(flatten)]`. When a field
631/// is marked with flatten, its type's fields are extracted and merged into the
632/// containing object.
633///
634/// # Panics
635///
636/// Panics if the TypeDef is not an Object (or Named wrapping an Object).
637pub fn extract_object_fields(typedef: &TypeDef) -> Vec<Field> {
638    match typedef {
639        TypeDef::Object(fields) => fields.clone(),
640        TypeDef::Named { def, .. } => extract_object_fields(def),
641        other => panic!(
642            "#[ts(flatten)] can only be used on fields with object types, got: {:?}",
643            other
644        ),
645    }
646}
647
648/// Extracts the inner type definition, unwrapping Named if necessary.
649///
650/// This is used by the derive macro to implement `#[ts(inline)]`. When a field
651/// is marked with inline, the full type definition is inlined instead of using
652/// a type reference.
653pub fn inline_typedef(typedef: TypeDef) -> TypeDef {
654    match typedef {
655        TypeDef::Named { def, .. } => *def,
656        other => other,
657    }
658}
659
660// ============================================================================
661// TYPE REGISTRY
662// ============================================================================
663
664use std::collections::{HashSet, VecDeque};
665
666/// A registry for collecting and managing TypeScript type definitions.
667///
668/// The TypeRegistry collects named types, resolves their dependency order,
669/// and renders them to a TypeScript file. This enables:
670/// - **Deduplication**: Each named type is emitted once
671/// - **Ordering**: Types are emitted in dependency order
672/// - **Output**: Generate valid .ts or .d.ts files
673///
674/// # Example
675///
676/// ```ignore
677/// use ferrotype::{TypeRegistry, TypeScript};
678///
679/// let mut registry = TypeRegistry::new();
680/// registry.register::<User>();
681/// registry.register::<Post>();
682///
683/// let output = registry.render();
684/// std::fs::write("types.ts", output)?;
685/// ```
686#[derive(Debug, Default)]
687pub struct TypeRegistry {
688    /// Named type definitions, keyed by name
689    types: HashMap<String, TypeDef>,
690    /// Order in which types were registered (for stable output when no deps)
691    registration_order: Vec<String>,
692}
693
694impl TypeRegistry {
695    /// Creates a new empty registry.
696    pub fn new() -> Self {
697        Self::default()
698    }
699
700    /// Creates a registry populated with all auto-registered types.
701    ///
702    /// This collects all types that were registered via the `#[derive(TypeScript)]`
703    /// macro using the distributed slice mechanism.
704    ///
705    /// # Example
706    ///
707    /// ```ignore
708    /// use ferrotype::{TypeRegistry, TypeScript};
709    ///
710    /// #[derive(TypeScript)]
711    /// struct User { name: String, age: u32 }
712    ///
713    /// #[derive(TypeScript)]
714    /// struct Post { title: String, author: User }
715    ///
716    /// // Collect all types automatically - no manual registration needed!
717    /// let registry = TypeRegistry::from_distributed();
718    /// println!("{}", registry.render());
719    /// ```
720    pub fn from_distributed() -> Self {
721        let mut registry = Self::new();
722        for type_fn in TYPESCRIPT_TYPES {
723            let typedef = type_fn();
724            registry.add_typedef(typedef);
725        }
726        registry
727    }
728
729    /// Collects all auto-registered types into this registry.
730    ///
731    /// This is useful when you want to add auto-registered types to an existing
732    /// registry that may already have some manually registered types.
733    ///
734    /// # Example
735    ///
736    /// ```ignore
737    /// let mut registry = TypeRegistry::new();
738    /// // Add some manual types first
739    /// registry.register::<SomeManualType>();
740    /// // Then collect all auto-registered types
741    /// registry.collect_all();
742    /// ```
743    pub fn collect_all(&mut self) {
744        for type_fn in TYPESCRIPT_TYPES {
745            let typedef = type_fn();
746            self.add_typedef(typedef);
747        }
748    }
749
750    /// Registers a type that implements TypeScript.
751    ///
752    /// This extracts all named types from the type definition and adds them
753    /// to the registry. Named types are deduplicated by name.
754    pub fn register<T: TypeScript>(&mut self) {
755        let typedef = T::typescript();
756        self.add_typedef(typedef);
757    }
758
759    /// Adds a TypeDef to the registry, extracting all named types.
760    pub fn add_typedef(&mut self, typedef: TypeDef) {
761        self.extract_named_types(&typedef);
762    }
763
764    /// Recursively extracts all Named types from a TypeDef.
765    fn extract_named_types(&mut self, typedef: &TypeDef) {
766        match typedef {
767            TypeDef::Named { namespace, name, def, .. } => {
768                // Use fully qualified name as the key (e.g., "VM.Git.State")
769                let qualified_name = if namespace.is_empty() {
770                    name.clone()
771                } else {
772                    format!("{}.{}", namespace.join("."), name)
773                };
774
775                if !self.types.contains_key(&qualified_name) {
776                    self.types.insert(qualified_name.clone(), typedef.clone());
777                    self.registration_order.push(qualified_name);
778                    // Also extract from the inner definition
779                    self.extract_named_types(def);
780                }
781            }
782            TypeDef::Array(inner) => self.extract_named_types(inner),
783            TypeDef::Tuple(items) => {
784                for item in items {
785                    self.extract_named_types(item);
786                }
787            }
788            TypeDef::Object(fields) => {
789                for field in fields {
790                    self.extract_named_types(&field.ty);
791                }
792            }
793            TypeDef::Union(items) | TypeDef::Intersection(items) => {
794                for item in items {
795                    self.extract_named_types(item);
796                }
797            }
798            TypeDef::Record { key, value } => {
799                self.extract_named_types(key);
800                self.extract_named_types(value);
801            }
802            TypeDef::Function { params, return_type } => {
803                for param in params {
804                    self.extract_named_types(&param.ty);
805                }
806                self.extract_named_types(return_type);
807            }
808            TypeDef::Generic { args, .. } => {
809                for arg in args {
810                    self.extract_named_types(arg);
811                }
812            }
813            TypeDef::TemplateLiteral { types, .. } => {
814                for ty in types {
815                    self.extract_named_types(ty);
816                }
817            }
818            TypeDef::GenericDef { name, type_params, def } => {
819                if !self.types.contains_key(name) {
820                    self.types.insert(name.clone(), typedef.clone());
821                    self.registration_order.push(name.clone());
822                    // Extract from type parameter constraints and defaults
823                    for param in type_params {
824                        if let Some(ref constraint) = param.constraint {
825                            self.extract_named_types(constraint);
826                        }
827                        if let Some(ref default) = param.default {
828                            self.extract_named_types(default);
829                        }
830                    }
831                    // Extract from the inner definition
832                    self.extract_named_types(def);
833                }
834            }
835            // IndexedAccess references a base type by name (similar to Ref)
836            // Primitives, Refs, Literals, TypeParamRefs, and IndexedAccess have no nested named types
837            TypeDef::Primitive(_) | TypeDef::Ref(_) | TypeDef::Literal(_) | TypeDef::IndexedAccess { .. } | TypeDef::TypeParamRef(_) => {}
838        }
839    }
840
841    /// Returns the number of registered types.
842    pub fn len(&self) -> usize {
843        self.types.len()
844    }
845
846    /// Returns true if no types are registered.
847    pub fn is_empty(&self) -> bool {
848        self.types.is_empty()
849    }
850
851    /// Returns the names of all registered types.
852    pub fn type_names(&self) -> impl Iterator<Item = &str> {
853        self.types.keys().map(|s| s.as_str())
854    }
855
856    /// Gets a type definition by name.
857    pub fn get(&self, name: &str) -> Option<&TypeDef> {
858        self.types.get(name)
859    }
860
861    /// Computes the dependencies for a type (what other named types it references).
862    fn get_dependencies(&self, typedef: &TypeDef) -> HashSet<String> {
863        let mut deps = HashSet::new();
864        self.collect_dependencies(typedef, &mut deps);
865        deps
866    }
867
868    /// Recursively collects dependencies from a TypeDef.
869    fn collect_dependencies(&self, typedef: &TypeDef, deps: &mut HashSet<String>) {
870        match typedef {
871            TypeDef::Named { def, .. } => {
872                // Don't add self as dependency, but check inner def
873                self.collect_dependencies(def, deps);
874            }
875            TypeDef::Ref(name) => {
876                if self.types.contains_key(name) {
877                    deps.insert(name.clone());
878                }
879            }
880            TypeDef::Array(inner) => self.collect_dependencies(inner, deps),
881            TypeDef::Tuple(items) => {
882                for item in items {
883                    self.collect_dependencies(item, deps);
884                }
885            }
886            TypeDef::Object(fields) => {
887                for field in fields {
888                    self.collect_dependencies(&field.ty, deps);
889                }
890            }
891            TypeDef::Union(variants) => {
892                for v in variants {
893                    self.collect_dependencies(v, deps);
894                }
895            }
896            TypeDef::Intersection(types) => {
897                for t in types {
898                    self.collect_dependencies(t, deps);
899                }
900            }
901            TypeDef::Record { key, value } => {
902                self.collect_dependencies(key, deps);
903                self.collect_dependencies(value, deps);
904            }
905            TypeDef::Function { params, return_type } => {
906                for param in params {
907                    self.collect_dependencies(&param.ty, deps);
908                }
909                self.collect_dependencies(return_type, deps);
910            }
911            TypeDef::Generic { base, args } => {
912                // The base generic type itself is a dependency
913                if self.types.contains_key(base) {
914                    deps.insert(base.clone());
915                }
916                for arg in args {
917                    self.collect_dependencies(arg, deps);
918                }
919            }
920            TypeDef::TemplateLiteral { types, .. } => {
921                for ty in types {
922                    self.collect_dependencies(ty, deps);
923                }
924            }
925            TypeDef::IndexedAccess { base, .. } => {
926                // The base type is a dependency
927                if self.types.contains_key(base) {
928                    deps.insert(base.clone());
929                }
930            }
931            TypeDef::GenericDef { type_params, def, .. } => {
932                // Don't add self as dependency, but check constraints, defaults, and inner def
933                for param in type_params {
934                    if let Some(ref constraint) = param.constraint {
935                        self.collect_dependencies(constraint, deps);
936                    }
937                    if let Some(ref default) = param.default {
938                        self.collect_dependencies(default, deps);
939                    }
940                }
941                self.collect_dependencies(def, deps);
942            }
943            // TypeParamRef references a type parameter, not a named type, so no dependency
944            TypeDef::Primitive(_) | TypeDef::Literal(_) | TypeDef::TypeParamRef(_) => {}
945        }
946    }
947
948    /// Returns types in dependency order (types with no dependencies first).
949    ///
950    /// Uses Kahn's algorithm for topological sort.
951    pub fn sorted_types(&self) -> Vec<&str> {
952        // Build dependency graph
953        let mut in_degree: HashMap<&str, usize> = HashMap::new();
954        let mut dependents: HashMap<&str, Vec<&str>> = HashMap::new();
955
956        // Initialize
957        for name in self.types.keys() {
958            in_degree.insert(name.as_str(), 0);
959            dependents.insert(name.as_str(), Vec::new());
960        }
961
962        // Calculate in-degrees and build reverse graph
963        for (name, typedef) in &self.types {
964            let deps = self.get_dependencies(typedef);
965            for dep in deps {
966                if let Some(dep_name) = self.types.get_key_value(&dep) {
967                    *in_degree.get_mut(name.as_str()).unwrap() += 1;
968                    dependents.get_mut(dep_name.0.as_str()).unwrap().push(name.as_str());
969                }
970            }
971        }
972
973        // Kahn's algorithm
974        let mut queue: VecDeque<&str> = VecDeque::new();
975        let mut result: Vec<&str> = Vec::new();
976
977        // Start with types that have no dependencies
978        for (name, &degree) in &in_degree {
979            if degree == 0 {
980                queue.push_back(name);
981            }
982        }
983
984        // Sort the initial queue by registration order for stable output
985        let mut initial: Vec<_> = queue.drain(..).collect();
986        initial.sort_by_key(|name| {
987            self.registration_order.iter().position(|n| n == *name).unwrap_or(usize::MAX)
988        });
989        queue.extend(initial);
990
991        while let Some(name) = queue.pop_front() {
992            result.push(name);
993
994            // Get dependents sorted by registration order for stable output
995            let mut deps: Vec<_> = dependents.get(name).map(|v| v.as_slice()).unwrap_or(&[]).to_vec();
996            deps.sort_by_key(|n| {
997                self.registration_order.iter().position(|name| name == *n).unwrap_or(usize::MAX)
998            });
999
1000            for dependent in deps {
1001                let degree = in_degree.get_mut(dependent).unwrap();
1002                *degree -= 1;
1003                if *degree == 0 {
1004                    queue.push_back(dependent);
1005                }
1006            }
1007        }
1008
1009        // If result doesn't contain all types, there's a cycle
1010        // Fall back to registration order for remaining types
1011        if result.len() < self.types.len() {
1012            for name in &self.registration_order {
1013                if !result.contains(&name.as_str()) {
1014                    result.push(name.as_str());
1015                }
1016            }
1017        }
1018
1019        result
1020    }
1021
1022    /// Renders all registered types to TypeScript declarations.
1023    ///
1024    /// Types are emitted in dependency order, with proper formatting.
1025    pub fn render(&self) -> String {
1026        let sorted = self.sorted_types();
1027        let mut output = String::new();
1028
1029        // Add header comment
1030        output.push_str("// Generated by ferrotype\n");
1031        output.push_str("// Do not edit manually\n\n");
1032
1033        for name in sorted {
1034            if let Some(typedef) = self.types.get(name) {
1035                output.push_str(&typedef.render_declaration());
1036                output.push_str("\n\n");
1037            }
1038        }
1039
1040        // Remove trailing newline
1041        output.trim_end().to_string() + "\n"
1042    }
1043
1044    /// Renders all registered types with `export` keywords.
1045    ///
1046    /// For namespaced types, exports the namespace declaration.
1047    pub fn render_exported(&self) -> String {
1048        let sorted = self.sorted_types();
1049        let mut output = String::new();
1050
1051        // Add header comment
1052        output.push_str("// Generated by ferrotype\n");
1053        output.push_str("// Do not edit manually\n\n");
1054
1055        for name in sorted {
1056            if let Some(typedef) = self.types.get(name) {
1057                match typedef {
1058                    TypeDef::Named { namespace, name, def, .. } => {
1059                        let inner = format!("export type {} = {};", name, def.render());
1060                        if namespace.is_empty() {
1061                            output.push_str(&inner);
1062                        } else {
1063                            // For namespaced types, wrap in export namespace
1064                            output.push_str(&Self::wrap_in_export_namespace(namespace, &inner));
1065                        }
1066                        output.push_str("\n\n");
1067                    }
1068                    TypeDef::GenericDef { name, type_params, def } => {
1069                        let params_str: Vec<_> = type_params.iter().map(|p| p.render()).collect();
1070                        output.push_str(&format!(
1071                            "export type {}<{}> = {};\n\n",
1072                            name,
1073                            params_str.join(", "),
1074                            def.render()
1075                        ));
1076                    }
1077                    _ => {}
1078                }
1079            }
1080        }
1081
1082        // Remove trailing newline
1083        output.trim_end().to_string() + "\n"
1084    }
1085
1086    /// Wraps a declaration in export namespace blocks.
1087    fn wrap_in_export_namespace(namespace: &[String], inner: &str) -> String {
1088        if namespace.is_empty() {
1089            return inner.to_string();
1090        }
1091
1092        let mut result = String::new();
1093        let indent = "    ";
1094
1095        // Opening export namespace declarations
1096        for (i, ns) in namespace.iter().enumerate() {
1097            for _ in 0..i {
1098                result.push_str(indent);
1099            }
1100            result.push_str("export namespace ");
1101            result.push_str(ns);
1102            result.push_str(" {\n");
1103        }
1104
1105        // Inner content with proper indentation
1106        let depth = namespace.len();
1107        for _ in 0..depth {
1108            result.push_str(indent);
1109        }
1110        result.push_str(inner);
1111        result.push('\n');
1112
1113        // Closing braces
1114        for i in (0..depth).rev() {
1115            for _ in 0..i {
1116                result.push_str(indent);
1117            }
1118            result.push('}');
1119            if i > 0 {
1120                result.push('\n');
1121            }
1122        }
1123
1124        result
1125    }
1126
1127    /// Clears all registered types.
1128    pub fn clear(&mut self) {
1129        self.types.clear();
1130        self.registration_order.clear();
1131    }
1132}
1133
1134// ============================================================================
1135// TypeScript TRAIT IMPLEMENTATIONS FOR PRIMITIVES
1136// ============================================================================
1137
1138impl TypeScript for () {
1139    fn typescript() -> TypeDef {
1140        TypeDef::Primitive(Primitive::Void)
1141    }
1142}
1143
1144impl TypeScript for bool {
1145    fn typescript() -> TypeDef {
1146        TypeDef::Primitive(Primitive::Boolean)
1147    }
1148}
1149
1150impl TypeScript for String {
1151    fn typescript() -> TypeDef {
1152        TypeDef::Primitive(Primitive::String)
1153    }
1154}
1155
1156impl TypeScript for &str {
1157    fn typescript() -> TypeDef {
1158        TypeDef::Primitive(Primitive::String)
1159    }
1160}
1161
1162impl TypeScript for char {
1163    fn typescript() -> TypeDef {
1164        TypeDef::Primitive(Primitive::String)
1165    }
1166}
1167
1168macro_rules! impl_typescript_number {
1169    ($($t:ty),*) => {
1170        $(
1171            impl TypeScript for $t {
1172                fn typescript() -> TypeDef {
1173                    TypeDef::Primitive(Primitive::Number)
1174                }
1175            }
1176        )*
1177    };
1178}
1179
1180impl_typescript_number!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64);
1181
1182// i128/u128 map to bigint in TypeScript
1183impl TypeScript for i128 {
1184    fn typescript() -> TypeDef {
1185        TypeDef::Primitive(Primitive::BigInt)
1186    }
1187}
1188
1189impl TypeScript for u128 {
1190    fn typescript() -> TypeDef {
1191        TypeDef::Primitive(Primitive::BigInt)
1192    }
1193}
1194
1195// ============================================================================
1196// TypeScript TRAIT IMPLEMENTATIONS FOR GENERIC TYPES
1197// ============================================================================
1198
1199impl<T: TypeScript> TypeScript for Option<T> {
1200    fn typescript() -> TypeDef {
1201        TypeDef::Union(vec![T::typescript(), TypeDef::Primitive(Primitive::Null)])
1202    }
1203}
1204
1205impl<T: TypeScript> TypeScript for Vec<T> {
1206    fn typescript() -> TypeDef {
1207        TypeDef::Array(Box::new(T::typescript()))
1208    }
1209}
1210
1211impl<T: TypeScript> TypeScript for Box<T> {
1212    fn typescript() -> TypeDef {
1213        T::typescript()
1214    }
1215}
1216
1217impl<T: TypeScript> TypeScript for std::rc::Rc<T> {
1218    fn typescript() -> TypeDef {
1219        T::typescript()
1220    }
1221}
1222
1223impl<T: TypeScript> TypeScript for std::sync::Arc<T> {
1224    fn typescript() -> TypeDef {
1225        T::typescript()
1226    }
1227}
1228
1229impl<T: TypeScript> TypeScript for std::cell::RefCell<T> {
1230    fn typescript() -> TypeDef {
1231        T::typescript()
1232    }
1233}
1234
1235impl<T: TypeScript> TypeScript for std::cell::Cell<T> {
1236    fn typescript() -> TypeDef {
1237        T::typescript()
1238    }
1239}
1240
1241impl<K: TypeScript, V: TypeScript> TypeScript for HashMap<K, V> {
1242    fn typescript() -> TypeDef {
1243        TypeDef::Record {
1244            key: Box::new(K::typescript()),
1245            value: Box::new(V::typescript()),
1246        }
1247    }
1248}
1249
1250impl<K: TypeScript, V: TypeScript> TypeScript for std::collections::BTreeMap<K, V> {
1251    fn typescript() -> TypeDef {
1252        TypeDef::Record {
1253            key: Box::new(K::typescript()),
1254            value: Box::new(V::typescript()),
1255        }
1256    }
1257}
1258
1259impl<T: TypeScript, E: TypeScript> TypeScript for Result<T, E> {
1260    fn typescript() -> TypeDef {
1261        TypeDef::Union(vec![
1262            TypeDef::Object(vec![
1263                Field::new("ok", TypeDef::Literal(Literal::Boolean(true))),
1264                Field::new("value", T::typescript()),
1265            ]),
1266            TypeDef::Object(vec![
1267                Field::new("ok", TypeDef::Literal(Literal::Boolean(false))),
1268                Field::new("error", E::typescript()),
1269            ]),
1270        ])
1271    }
1272}
1273
1274// ============================================================================
1275// TypeScript TRAIT IMPLEMENTATIONS FOR TUPLES
1276// ============================================================================
1277
1278impl<A: TypeScript> TypeScript for (A,) {
1279    fn typescript() -> TypeDef {
1280        TypeDef::Tuple(vec![A::typescript()])
1281    }
1282}
1283
1284impl<A: TypeScript, B: TypeScript> TypeScript for (A, B) {
1285    fn typescript() -> TypeDef {
1286        TypeDef::Tuple(vec![A::typescript(), B::typescript()])
1287    }
1288}
1289
1290impl<A: TypeScript, B: TypeScript, C: TypeScript> TypeScript for (A, B, C) {
1291    fn typescript() -> TypeDef {
1292        TypeDef::Tuple(vec![A::typescript(), B::typescript(), C::typescript()])
1293    }
1294}
1295
1296impl<A: TypeScript, B: TypeScript, C: TypeScript, D: TypeScript> TypeScript for (A, B, C, D) {
1297    fn typescript() -> TypeDef {
1298        TypeDef::Tuple(vec![
1299            A::typescript(),
1300            B::typescript(),
1301            C::typescript(),
1302            D::typescript(),
1303        ])
1304    }
1305}
1306
1307impl<A: TypeScript, B: TypeScript, C: TypeScript, D: TypeScript, E: TypeScript> TypeScript
1308    for (A, B, C, D, E)
1309{
1310    fn typescript() -> TypeDef {
1311        TypeDef::Tuple(vec![
1312            A::typescript(),
1313            B::typescript(),
1314            C::typescript(),
1315            D::typescript(),
1316            E::typescript(),
1317        ])
1318    }
1319}
1320
1321impl<A: TypeScript, B: TypeScript, C: TypeScript, D: TypeScript, E: TypeScript, F: TypeScript>
1322    TypeScript for (A, B, C, D, E, F)
1323{
1324    fn typescript() -> TypeDef {
1325        TypeDef::Tuple(vec![
1326            A::typescript(),
1327            B::typescript(),
1328            C::typescript(),
1329            D::typescript(),
1330            E::typescript(),
1331            F::typescript(),
1332        ])
1333    }
1334}
1335
1336// ============================================================================
1337// TESTS
1338// ============================================================================
1339
1340#[cfg(test)]
1341mod tests {
1342    use super::*;
1343
1344    // ========================================================================
1345    // TypeScript TRAIT + TypeDef TESTS
1346    // ========================================================================
1347
1348    #[test]
1349    fn test_typedef_primitive_render() {
1350        assert_eq!(TypeDef::Primitive(Primitive::String).render(), "string");
1351        assert_eq!(TypeDef::Primitive(Primitive::Number).render(), "number");
1352        assert_eq!(TypeDef::Primitive(Primitive::Boolean).render(), "boolean");
1353        assert_eq!(TypeDef::Primitive(Primitive::Null).render(), "null");
1354        assert_eq!(TypeDef::Primitive(Primitive::Undefined).render(), "undefined");
1355        assert_eq!(TypeDef::Primitive(Primitive::Void).render(), "void");
1356        assert_eq!(TypeDef::Primitive(Primitive::Never).render(), "never");
1357        assert_eq!(TypeDef::Primitive(Primitive::Any).render(), "any");
1358        assert_eq!(TypeDef::Primitive(Primitive::Unknown).render(), "unknown");
1359        assert_eq!(TypeDef::Primitive(Primitive::BigInt).render(), "bigint");
1360    }
1361
1362    #[test]
1363    fn test_typedef_array_render() {
1364        let arr = TypeDef::Array(Box::new(TypeDef::Primitive(Primitive::String)));
1365        assert_eq!(arr.render(), "string[]");
1366
1367        // Union in array should be wrapped in parens
1368        let union_arr = TypeDef::Array(Box::new(TypeDef::Union(vec![
1369            TypeDef::Primitive(Primitive::String),
1370            TypeDef::Primitive(Primitive::Number),
1371        ])));
1372        assert_eq!(union_arr.render(), "(string | number)[]");
1373    }
1374
1375    #[test]
1376    fn test_typedef_tuple_render() {
1377        let tuple = TypeDef::Tuple(vec![
1378            TypeDef::Primitive(Primitive::String),
1379            TypeDef::Primitive(Primitive::Number),
1380        ]);
1381        assert_eq!(tuple.render(), "[string, number]");
1382    }
1383
1384    #[test]
1385    fn test_typedef_object_render() {
1386        let obj = TypeDef::Object(vec![
1387            Field::new("name", TypeDef::Primitive(Primitive::String)),
1388            Field::optional("age", TypeDef::Primitive(Primitive::Number)),
1389        ]);
1390        assert_eq!(obj.render(), "{ name: string; age?: number }");
1391
1392        let empty_obj = TypeDef::Object(vec![]);
1393        assert_eq!(empty_obj.render(), "{}");
1394    }
1395
1396    #[test]
1397    fn test_typedef_object_readonly_field() {
1398        let obj = TypeDef::Object(vec![
1399            Field::new("id", TypeDef::Primitive(Primitive::String)).readonly(),
1400        ]);
1401        assert_eq!(obj.render(), "{ readonly id: string }");
1402    }
1403
1404    #[test]
1405    fn test_typedef_union_render() {
1406        let union = TypeDef::Union(vec![
1407            TypeDef::Primitive(Primitive::String),
1408            TypeDef::Primitive(Primitive::Number),
1409            TypeDef::Primitive(Primitive::Null),
1410        ]);
1411        assert_eq!(union.render(), "string | number | null");
1412    }
1413
1414    #[test]
1415    fn test_typedef_intersection_render() {
1416        let intersection = TypeDef::Intersection(vec![
1417            TypeDef::Ref("Base".into()),
1418            TypeDef::Object(vec![
1419                Field::new("extra", TypeDef::Primitive(Primitive::String)),
1420            ]),
1421        ]);
1422        assert_eq!(intersection.render(), "Base & { extra: string }");
1423    }
1424
1425    #[test]
1426    fn test_typedef_record_render() {
1427        let record = TypeDef::Record {
1428            key: Box::new(TypeDef::Primitive(Primitive::String)),
1429            value: Box::new(TypeDef::Primitive(Primitive::Number)),
1430        };
1431        assert_eq!(record.render(), "Record<string, number>");
1432    }
1433
1434    #[test]
1435    fn test_typedef_named_render() {
1436        let named = TypeDef::Named {
1437            namespace: vec![],
1438            name: "UserId".into(),
1439            def: Box::new(TypeDef::Primitive(Primitive::String)),
1440            module: None,
1441        };
1442        // Named types render as just their name (for inline use)
1443        assert_eq!(named.render(), "UserId");
1444        // Full declaration uses render_declaration
1445        assert_eq!(named.render_declaration(), "type UserId = string;");
1446    }
1447
1448    #[test]
1449    fn test_typedef_namespaced_render() {
1450        // VM.Git.State pattern
1451        let namespaced = TypeDef::Named {
1452            namespace: vec!["VM".into(), "Git".into()],
1453            name: "State".into(),
1454            def: Box::new(TypeDef::Union(vec![
1455                TypeDef::Literal(Literal::String("clean".into())),
1456                TypeDef::Literal(Literal::String("dirty".into())),
1457                TypeDef::Literal(Literal::String("unknown".into())),
1458            ])),
1459            module: None,
1460        };
1461        // Inline reference includes namespace path
1462        assert_eq!(namespaced.render(), "VM.Git.State");
1463        // Declaration wraps in namespace blocks
1464        let decl = namespaced.render_declaration();
1465        assert!(decl.contains("namespace VM {"));
1466        assert!(decl.contains("namespace Git {"));
1467        assert!(decl.contains(r#"type State = "clean" | "dirty" | "unknown";"#));
1468    }
1469
1470    #[test]
1471    fn test_typedef_single_namespace_render() {
1472        let namespaced = TypeDef::Named {
1473            namespace: vec!["API".into()],
1474            name: "Response".into(),
1475            def: Box::new(TypeDef::Primitive(Primitive::String)),
1476            module: None,
1477        };
1478        assert_eq!(namespaced.render(), "API.Response");
1479        let decl = namespaced.render_declaration();
1480        assert!(decl.contains("namespace API {"));
1481        assert!(decl.contains("type Response = string;"));
1482    }
1483
1484    #[test]
1485    fn test_typedef_ref_render() {
1486        let ref_type = TypeDef::Ref("User".into());
1487        assert_eq!(ref_type.render(), "User");
1488    }
1489
1490    #[test]
1491    fn test_typedef_literal_render() {
1492        assert_eq!(TypeDef::Literal(Literal::String("foo".into())).render(), "\"foo\"");
1493        assert_eq!(TypeDef::Literal(Literal::Number(42.0)).render(), "42");
1494        assert_eq!(TypeDef::Literal(Literal::Number(3.14)).render(), "3.14");
1495        assert_eq!(TypeDef::Literal(Literal::Boolean(true)).render(), "true");
1496        assert_eq!(TypeDef::Literal(Literal::Boolean(false)).render(), "false");
1497    }
1498
1499    #[test]
1500    fn test_typedef_literal_escaping() {
1501        let lit = Literal::String("say \"hello\"".into());
1502        assert_eq!(lit.render(), "\"say \\\"hello\\\"\"");
1503    }
1504
1505    #[test]
1506    fn test_typedef_function_render() {
1507        let func = TypeDef::Function {
1508            params: vec![
1509                Field::new("name", TypeDef::Primitive(Primitive::String)),
1510                Field::new("age", TypeDef::Primitive(Primitive::Number)),
1511            ],
1512            return_type: Box::new(TypeDef::Primitive(Primitive::Void)),
1513        };
1514        assert_eq!(func.render(), "(name: string, age: number) => void");
1515    }
1516
1517    #[test]
1518    fn test_typedef_generic_render() {
1519        let generic = TypeDef::Generic {
1520            base: "Promise".into(),
1521            args: vec![TypeDef::Primitive(Primitive::String)],
1522        };
1523        assert_eq!(generic.render(), "Promise<string>");
1524
1525        let multi_generic = TypeDef::Generic {
1526            base: "Map".into(),
1527            args: vec![
1528                TypeDef::Primitive(Primitive::String),
1529                TypeDef::Primitive(Primitive::Number),
1530            ],
1531        };
1532        assert_eq!(multi_generic.render(), "Map<string, number>");
1533    }
1534
1535    #[test]
1536    fn test_typedef_template_literal_render() {
1537        // Simple pattern: `vm-${string}`
1538        let vm_id = TypeDef::TemplateLiteral {
1539            strings: vec!["vm-".into(), "".into()],
1540            types: vec![Box::new(TypeDef::Primitive(Primitive::String))],
1541        };
1542        assert_eq!(vm_id.render(), "`vm-${string}`");
1543
1544        // Multiple placeholders: `v${number}.${number}.${number}`
1545        let semver = TypeDef::TemplateLiteral {
1546            strings: vec!["v".into(), ".".into(), ".".into(), "".into()],
1547            types: vec![
1548                Box::new(TypeDef::Primitive(Primitive::Number)),
1549                Box::new(TypeDef::Primitive(Primitive::Number)),
1550                Box::new(TypeDef::Primitive(Primitive::Number)),
1551            ],
1552        };
1553        assert_eq!(semver.render(), "`v${number}.${number}.${number}`");
1554
1555        // API route pattern: `/api/${string}`
1556        let api_route = TypeDef::TemplateLiteral {
1557            strings: vec!["/api/".into(), "".into()],
1558            types: vec![Box::new(TypeDef::Primitive(Primitive::String))],
1559        };
1560        assert_eq!(api_route.render(), "`/api/${string}`");
1561
1562        // Prefix and suffix: `user-${string}-id`
1563        let complex_id = TypeDef::TemplateLiteral {
1564            strings: vec!["user-".into(), "-id".into()],
1565            types: vec![Box::new(TypeDef::Primitive(Primitive::String))],
1566        };
1567        assert_eq!(complex_id.render(), "`user-${string}-id`");
1568
1569        // No placeholders (just a string)
1570        let static_str = TypeDef::TemplateLiteral {
1571            strings: vec!["static-value".into()],
1572            types: vec![],
1573        };
1574        assert_eq!(static_str.render(), "`static-value`");
1575    }
1576
1577    #[test]
1578    fn test_typedef_template_literal_escaping() {
1579        // Test escaping backticks
1580        let with_backtick = TypeDef::TemplateLiteral {
1581            strings: vec!["code: `".into(), "`".into()],
1582            types: vec![Box::new(TypeDef::Primitive(Primitive::String))],
1583        };
1584        assert_eq!(with_backtick.render(), "`code: \\`${string}\\``");
1585
1586        // Test escaping backslashes
1587        let with_backslash = TypeDef::TemplateLiteral {
1588            strings: vec!["path\\to\\".into(), "".into()],
1589            types: vec![Box::new(TypeDef::Primitive(Primitive::String))],
1590        };
1591        assert_eq!(with_backslash.render(), "`path\\\\to\\\\${string}`");
1592    }
1593
1594    #[test]
1595    fn test_typedef_template_literal_with_refs() {
1596        // Template literal with type reference
1597        let typed_id = TypeDef::TemplateLiteral {
1598            strings: vec!["vm-".into(), "".into()],
1599            types: vec![Box::new(TypeDef::Ref("VmIdSuffix".into()))],
1600        };
1601        assert_eq!(typed_id.render(), "`vm-${VmIdSuffix}`");
1602    }
1603
1604    #[test]
1605    fn test_typedef_indexed_access_render() {
1606        // Basic indexed access: Profile["login"]
1607        let indexed = TypeDef::IndexedAccess {
1608            base: "Profile".into(),
1609            key: "login".into(),
1610        };
1611        assert_eq!(indexed.render(), "Profile[\"login\"]");
1612
1613        // Nested namespace: User.Settings["theme"]
1614        let nested = TypeDef::IndexedAccess {
1615            base: "User.Settings".into(),
1616            key: "theme".into(),
1617        };
1618        assert_eq!(nested.render(), "User.Settings[\"theme\"]");
1619    }
1620
1621    #[test]
1622    fn test_typedef_indexed_access_escaping() {
1623        // Key with quotes
1624        let with_quotes = TypeDef::IndexedAccess {
1625            base: "Config".into(),
1626            key: "key\"with\"quotes".into(),
1627        };
1628        assert_eq!(with_quotes.render(), "Config[\"key\\\"with\\\"quotes\"]");
1629
1630        // Key with backslashes
1631        let with_backslash = TypeDef::IndexedAccess {
1632            base: "Config".into(),
1633            key: "path\\to\\key".into(),
1634        };
1635        assert_eq!(with_backslash.render(), "Config[\"path\\\\to\\\\key\"]");
1636    }
1637
1638    #[test]
1639    fn test_typescript_trait_primitives() {
1640        assert_eq!(<()>::typescript().render(), "void");
1641        assert_eq!(bool::typescript().render(), "boolean");
1642        assert_eq!(String::typescript().render(), "string");
1643        assert_eq!(i32::typescript().render(), "number");
1644        assert_eq!(f64::typescript().render(), "number");
1645        assert_eq!(i128::typescript().render(), "bigint");
1646        assert_eq!(u128::typescript().render(), "bigint");
1647    }
1648
1649    #[test]
1650    fn test_typescript_trait_option() {
1651        let opt = <Option<String>>::typescript();
1652        assert_eq!(opt.render(), "string | null");
1653    }
1654
1655    #[test]
1656    fn test_typescript_trait_vec() {
1657        let vec_type = <Vec<i32>>::typescript();
1658        assert_eq!(vec_type.render(), "number[]");
1659    }
1660
1661    #[test]
1662    fn test_typescript_trait_hashmap() {
1663        let map = <HashMap<String, i32>>::typescript();
1664        assert_eq!(map.render(), "Record<string, number>");
1665    }
1666
1667    #[test]
1668    fn test_typescript_trait_result() {
1669        let result = <Result<String, String>>::typescript();
1670        assert_eq!(
1671            result.render(),
1672            "{ ok: true; value: string } | { ok: false; error: string }"
1673        );
1674    }
1675
1676    #[test]
1677    fn test_typescript_trait_tuples() {
1678        assert_eq!(<(String,)>::typescript().render(), "[string]");
1679        assert_eq!(<(String, i32)>::typescript().render(), "[string, number]");
1680        assert_eq!(
1681            <(String, i32, bool)>::typescript().render(),
1682            "[string, number, boolean]"
1683        );
1684    }
1685
1686    #[test]
1687    fn test_typescript_trait_box() {
1688        // Box<T> should be transparent
1689        assert_eq!(<Box<String>>::typescript().render(), "string");
1690    }
1691
1692    #[test]
1693    fn test_typedef_equality() {
1694        let a = TypeDef::Primitive(Primitive::String);
1695        let b = TypeDef::Primitive(Primitive::String);
1696        let c = TypeDef::Primitive(Primitive::Number);
1697        assert_eq!(a, b);
1698        assert_ne!(a, c);
1699    }
1700
1701    #[test]
1702    fn test_field_builder() {
1703        let field = Field::new("name", TypeDef::Primitive(Primitive::String));
1704        assert!(!field.optional);
1705        assert!(!field.readonly);
1706
1707        let opt_field = Field::optional("name", TypeDef::Primitive(Primitive::String));
1708        assert!(opt_field.optional);
1709
1710        let readonly_field = Field::new("id", TypeDef::Primitive(Primitive::String)).readonly();
1711        assert!(readonly_field.readonly);
1712    }
1713
1714    // ========================================================================
1715    // TYPE REGISTRY TESTS
1716    // ========================================================================
1717
1718    #[test]
1719    fn test_registry_new() {
1720        let registry = TypeRegistry::new();
1721        assert!(registry.is_empty());
1722        assert_eq!(registry.len(), 0);
1723    }
1724
1725    #[test]
1726    fn test_registry_add_typedef() {
1727        let mut registry = TypeRegistry::new();
1728
1729        let user_type = TypeDef::Named {
1730            namespace: vec![],
1731            name: "User".to_string(),
1732            def: Box::new(TypeDef::Object(vec![
1733                Field::new("id", TypeDef::Primitive(Primitive::String)),
1734                Field::new("name", TypeDef::Primitive(Primitive::String)),
1735            ])),
1736            module: None,
1737        };
1738
1739        registry.add_typedef(user_type);
1740
1741        assert_eq!(registry.len(), 1);
1742        assert!(registry.get("User").is_some());
1743    }
1744
1745    #[test]
1746    fn test_registry_deduplication() {
1747        let mut registry = TypeRegistry::new();
1748
1749        let user_type = TypeDef::Named {
1750            namespace: vec![],
1751            name: "User".to_string(),
1752            def: Box::new(TypeDef::Primitive(Primitive::String)),
1753            module: None,
1754        };
1755
1756        registry.add_typedef(user_type.clone());
1757        registry.add_typedef(user_type);
1758
1759        // Should only have one User type
1760        assert_eq!(registry.len(), 1);
1761    }
1762
1763    #[test]
1764    fn test_registry_extracts_nested_types() {
1765        let mut registry = TypeRegistry::new();
1766
1767        // UserId type (no dependencies)
1768        let user_id = TypeDef::Named {
1769            namespace: vec![],
1770            name: "UserId".to_string(),
1771            def: Box::new(TypeDef::Primitive(Primitive::String)),
1772            module: None,
1773        };
1774
1775        // User type depends on UserId via Ref
1776        let user = TypeDef::Named {
1777            namespace: vec![],
1778            name: "User".to_string(),
1779            def: Box::new(TypeDef::Object(vec![
1780                Field::new("id", TypeDef::Ref("UserId".to_string())),
1781                Field::new("name", TypeDef::Primitive(Primitive::String)),
1782            ])),
1783            module: None,
1784        };
1785
1786        // Post type that references User type
1787        let post_type = TypeDef::Named {
1788            namespace: vec![],
1789            name: "Post".to_string(),
1790            def: Box::new(TypeDef::Object(vec![
1791                Field::new("title", TypeDef::Primitive(Primitive::String)),
1792                Field::new("author", user),
1793            ])),
1794            module: None,
1795        };
1796
1797        registry.add_typedef(post_type);
1798        registry.add_typedef(user_id);
1799
1800        // Should have Post, User, and UserId
1801        assert_eq!(registry.len(), 3);
1802        assert!(registry.get("Post").is_some());
1803        assert!(registry.get("User").is_some());
1804        assert!(registry.get("UserId").is_some());
1805    }
1806
1807    #[test]
1808    fn test_registry_render() {
1809        let mut registry = TypeRegistry::new();
1810
1811        let user_type = TypeDef::Named {
1812            namespace: vec![],
1813            name: "User".to_string(),
1814            def: Box::new(TypeDef::Object(vec![
1815                Field::new("id", TypeDef::Primitive(Primitive::String)),
1816                Field::new("name", TypeDef::Primitive(Primitive::String)),
1817            ])),
1818            module: None,
1819        };
1820
1821        registry.add_typedef(user_type);
1822
1823        let output = registry.render();
1824        assert!(output.contains("// Generated by ferrotype"));
1825        assert!(output.contains("type User = { id: string; name: string };"));
1826    }
1827
1828    #[test]
1829    fn test_registry_render_exported() {
1830        let mut registry = TypeRegistry::new();
1831
1832        let user_type = TypeDef::Named {
1833            namespace: vec![],
1834            name: "User".to_string(),
1835            def: Box::new(TypeDef::Primitive(Primitive::String)),
1836            module: None,
1837        };
1838
1839        registry.add_typedef(user_type);
1840
1841        let output = registry.render_exported();
1842        assert!(output.contains("export type User = string;"));
1843    }
1844
1845    #[test]
1846    fn test_registry_dependency_order() {
1847        let mut registry = TypeRegistry::new();
1848
1849        // UserId type (no dependencies)
1850        let user_id = TypeDef::Named {
1851            namespace: vec![],
1852            name: "UserId".to_string(),
1853            def: Box::new(TypeDef::Primitive(Primitive::String)),
1854            module: None,
1855        };
1856
1857        // User type depends on UserId via Ref
1858        let user = TypeDef::Named {
1859            namespace: vec![],
1860            name: "User".to_string(),
1861            def: Box::new(TypeDef::Object(vec![
1862                Field::new("id", TypeDef::Ref("UserId".to_string())),
1863                Field::new("name", TypeDef::Primitive(Primitive::String)),
1864            ])),
1865            module: None,
1866        };
1867
1868        // Add in reverse order (User before UserId)
1869        registry.add_typedef(user);
1870        registry.add_typedef(user_id);
1871
1872        let sorted = registry.sorted_types();
1873
1874        // UserId should come before User
1875        let user_id_pos = sorted.iter().position(|&n| n == "UserId").unwrap();
1876        let user_pos = sorted.iter().position(|&n| n == "User").unwrap();
1877        assert!(user_id_pos < user_pos, "UserId should come before User");
1878    }
1879
1880    #[test]
1881    fn test_registry_clear() {
1882        let mut registry = TypeRegistry::new();
1883
1884        let user_type = TypeDef::Named {
1885            namespace: vec![],
1886            name: "User".to_string(),
1887            def: Box::new(TypeDef::Primitive(Primitive::String)),
1888            module: None,
1889        };
1890
1891        registry.add_typedef(user_type);
1892        assert_eq!(registry.len(), 1);
1893
1894        registry.clear();
1895        assert!(registry.is_empty());
1896    }
1897
1898    #[test]
1899    fn test_registry_type_names() {
1900        let mut registry = TypeRegistry::new();
1901
1902        registry.add_typedef(TypeDef::Named {
1903            namespace: vec![],
1904            name: "Alpha".to_string(),
1905            def: Box::new(TypeDef::Primitive(Primitive::String)),
1906            module: None,
1907        });
1908        registry.add_typedef(TypeDef::Named {
1909            namespace: vec![],
1910            name: "Beta".to_string(),
1911            def: Box::new(TypeDef::Primitive(Primitive::Number)),
1912            module: None,
1913        });
1914
1915        let names: Vec<_> = registry.type_names().collect();
1916        assert_eq!(names.len(), 2);
1917        assert!(names.contains(&"Alpha"));
1918        assert!(names.contains(&"Beta"));
1919    }
1920
1921    #[test]
1922    fn test_registry_complex_dependencies() {
1923        let mut registry = TypeRegistry::new();
1924
1925        // A -> B -> C (C should come first)
1926        let c = TypeDef::Named {
1927            namespace: vec![],
1928            name: "C".to_string(),
1929            def: Box::new(TypeDef::Primitive(Primitive::String)),
1930            module: None,
1931        };
1932
1933        let b = TypeDef::Named {
1934            namespace: vec![],
1935            name: "B".to_string(),
1936            def: Box::new(TypeDef::Object(vec![
1937                Field::new("c", TypeDef::Ref("C".to_string())),
1938            ])),
1939            module: None,
1940        };
1941
1942        let a = TypeDef::Named {
1943            namespace: vec![],
1944            name: "A".to_string(),
1945            def: Box::new(TypeDef::Object(vec![
1946                Field::new("b", TypeDef::Ref("B".to_string())),
1947            ])),
1948            module: None,
1949        };
1950
1951        // Add in wrong order
1952        registry.add_typedef(a);
1953        registry.add_typedef(b);
1954        registry.add_typedef(c);
1955
1956        let sorted = registry.sorted_types();
1957
1958        let c_pos = sorted.iter().position(|&n| n == "C").unwrap();
1959        let b_pos = sorted.iter().position(|&n| n == "B").unwrap();
1960        let a_pos = sorted.iter().position(|&n| n == "A").unwrap();
1961
1962        assert!(c_pos < b_pos, "C should come before B");
1963        assert!(b_pos < a_pos, "B should come before A");
1964    }
1965
1966    #[test]
1967    fn test_registry_indexed_access_dependency() {
1968        let mut registry = TypeRegistry::new();
1969
1970        // Profile type (no dependencies)
1971        let profile = TypeDef::Named {
1972            namespace: vec![],
1973            name: "Profile".to_string(),
1974            def: Box::new(TypeDef::Object(vec![
1975                Field::new("login", TypeDef::Primitive(Primitive::String)),
1976                Field::new("email", TypeDef::Primitive(Primitive::String)),
1977            ])),
1978            module: None,
1979        };
1980
1981        // UserLogin type depends on Profile via IndexedAccess
1982        let user_login = TypeDef::Named {
1983            namespace: vec![],
1984            name: "UserLogin".to_string(),
1985            def: Box::new(TypeDef::IndexedAccess {
1986                base: "Profile".to_string(),
1987                key: "login".to_string(),
1988            }),
1989            module: None,
1990        };
1991
1992        // Add in wrong order
1993        registry.add_typedef(user_login);
1994        registry.add_typedef(profile);
1995
1996        let sorted = registry.sorted_types();
1997
1998        let profile_pos = sorted.iter().position(|&n| n == "Profile").unwrap();
1999        let user_login_pos = sorted.iter().position(|&n| n == "UserLogin").unwrap();
2000
2001        assert!(profile_pos < user_login_pos, "Profile should come before UserLogin");
2002    }
2003
2004    // ========================================================================
2005    // AUTO-REGISTRATION TESTS
2006    // ========================================================================
2007
2008    // Test types for auto-registration
2009    #[derive(Debug)]
2010    struct AutoRegTestUser {
2011        name: String,
2012        age: u32,
2013    }
2014
2015    impl TypeScript for AutoRegTestUser {
2016        fn typescript() -> TypeDef {
2017            TypeDef::Named {
2018                namespace: vec![],
2019                name: "AutoRegTestUser".to_string(),
2020                def: Box::new(TypeDef::Object(vec![
2021                    Field::new("name", TypeDef::Primitive(Primitive::String)),
2022                    Field::new("age", TypeDef::Primitive(Primitive::Number)),
2023                ])),
2024                module: None,
2025            }
2026        }
2027    }
2028
2029    // Register manually for this test (the derive macro does this automatically)
2030    #[linkme::distributed_slice(TYPESCRIPT_TYPES)]
2031    static __TEST_REGISTER_USER: fn() -> TypeDef = || AutoRegTestUser::typescript();
2032
2033    #[test]
2034    fn test_from_distributed_collects_types() {
2035        let registry = TypeRegistry::from_distributed();
2036
2037        // The registry should contain our test type
2038        assert!(registry.get("AutoRegTestUser").is_some(),
2039            "Registry should contain AutoRegTestUser");
2040    }
2041
2042    #[test]
2043    fn test_collect_all_adds_to_existing() {
2044        let mut registry = TypeRegistry::new();
2045
2046        // Add a manual type first
2047        let manual_type = TypeDef::Named {
2048            namespace: vec![],
2049            name: "ManualType".to_string(),
2050            def: Box::new(TypeDef::Primitive(Primitive::String)),
2051            module: None,
2052        };
2053        registry.add_typedef(manual_type);
2054
2055        // Then collect all auto-registered types
2056        registry.collect_all();
2057
2058        // Should have both the manual type and auto-registered types
2059        assert!(registry.get("ManualType").is_some(),
2060            "Registry should contain ManualType");
2061        assert!(registry.get("AutoRegTestUser").is_some(),
2062            "Registry should contain AutoRegTestUser from distributed slice");
2063    }
2064
2065    #[test]
2066    fn test_distributed_slice_is_accessible() {
2067        // Verify the distributed slice can be iterated
2068        let count = TYPESCRIPT_TYPES.len();
2069        // At minimum, we have our test type registered
2070        assert!(count >= 1, "TYPESCRIPT_TYPES should have at least 1 entry");
2071    }
2072
2073    // ========================================================================
2074    // GENERIC TYPE DEFINITION TESTS (Core<T> Pattern)
2075    // ========================================================================
2076
2077    #[test]
2078    fn test_type_param_simple() {
2079        let param = TypeParam::new("T");
2080        assert_eq!(param.render(), "T");
2081    }
2082
2083    #[test]
2084    fn test_type_param_with_constraint() {
2085        let param = TypeParam::new("T").with_constraint(TypeDef::Primitive(Primitive::String));
2086        assert_eq!(param.render(), "T extends string");
2087    }
2088
2089    #[test]
2090    fn test_type_param_with_object_constraint() {
2091        let param = TypeParam::new("T").with_constraint(TypeDef::Object(vec![
2092            Field::new("type", TypeDef::Primitive(Primitive::String)),
2093        ]));
2094        assert_eq!(param.render(), "T extends { type: string }");
2095    }
2096
2097    #[test]
2098    fn test_type_param_with_default() {
2099        let param = TypeParam::new("T").with_default(TypeDef::Primitive(Primitive::Never));
2100        assert_eq!(param.render(), "T = never");
2101    }
2102
2103    #[test]
2104    fn test_type_param_with_constraint_and_default() {
2105        let param = TypeParam::new("T")
2106            .with_constraint(TypeDef::Primitive(Primitive::String))
2107            .with_default(TypeDef::Literal(Literal::String("default".into())));
2108        assert_eq!(param.render(), "T extends string = \"default\"");
2109    }
2110
2111    #[test]
2112    fn test_type_param_ref_render() {
2113        let param_ref = TypeDef::TypeParamRef("T".into());
2114        assert_eq!(param_ref.render(), "T");
2115    }
2116
2117    #[test]
2118    fn test_generic_def_simple() {
2119        // type Identity<T> = T;
2120        let generic_def = TypeDef::GenericDef {
2121            name: "Identity".into(),
2122            type_params: vec![TypeParam::new("T")],
2123            def: Box::new(TypeDef::TypeParamRef("T".into())),
2124        };
2125        assert_eq!(generic_def.render(), "Identity");
2126        assert_eq!(generic_def.render_declaration(), "type Identity<T> = T;");
2127    }
2128
2129    #[test]
2130    fn test_generic_def_with_constraint() {
2131        // type Wrapper<T extends { type: string }> = { data: T };
2132        let generic_def = TypeDef::GenericDef {
2133            name: "Wrapper".into(),
2134            type_params: vec![TypeParam::new("T").with_constraint(TypeDef::Object(vec![
2135                Field::new("type", TypeDef::Primitive(Primitive::String)),
2136            ]))],
2137            def: Box::new(TypeDef::Object(vec![
2138                Field::new("data", TypeDef::TypeParamRef("T".into())),
2139            ])),
2140        };
2141        assert_eq!(
2142            generic_def.render_declaration(),
2143            "type Wrapper<T extends { type: string }> = { data: T };"
2144        );
2145    }
2146
2147    #[test]
2148    fn test_generic_def_core_pattern() {
2149        // The Core<T> pattern for rich discriminated unions:
2150        // type Core<T extends { type: string }> = { id: string; timestamp: number; data: T };
2151        let core_def = TypeDef::GenericDef {
2152            name: "Core".into(),
2153            type_params: vec![TypeParam::new("T").with_constraint(TypeDef::Object(vec![
2154                Field::new("type", TypeDef::Primitive(Primitive::String)),
2155            ]))],
2156            def: Box::new(TypeDef::Object(vec![
2157                Field::new("id", TypeDef::Primitive(Primitive::String)),
2158                Field::new("timestamp", TypeDef::Primitive(Primitive::Number)),
2159                Field::new("data", TypeDef::TypeParamRef("T".into())),
2160            ])),
2161        };
2162
2163        assert_eq!(
2164            core_def.render_declaration(),
2165            "type Core<T extends { type: string }> = { id: string; timestamp: number; data: T };"
2166        );
2167    }
2168
2169    #[test]
2170    fn test_generic_def_multiple_params() {
2171        // type Pair<K, V> = { key: K; value: V };
2172        let pair_def = TypeDef::GenericDef {
2173            name: "Pair".into(),
2174            type_params: vec![TypeParam::new("K"), TypeParam::new("V")],
2175            def: Box::new(TypeDef::Object(vec![
2176                Field::new("key", TypeDef::TypeParamRef("K".into())),
2177                Field::new("value", TypeDef::TypeParamRef("V".into())),
2178            ])),
2179        };
2180        assert_eq!(
2181            pair_def.render_declaration(),
2182            "type Pair<K, V> = { key: K; value: V };"
2183        );
2184    }
2185
2186    #[test]
2187    fn test_generic_def_with_default_params() {
2188        // type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };
2189        let result_def = TypeDef::GenericDef {
2190            name: "Result".into(),
2191            type_params: vec![
2192                TypeParam::new("T"),
2193                TypeParam::new("E").with_default(TypeDef::Ref("Error".into())),
2194            ],
2195            def: Box::new(TypeDef::Union(vec![
2196                TypeDef::Object(vec![
2197                    Field::new("ok", TypeDef::Literal(Literal::Boolean(true))),
2198                    Field::new("value", TypeDef::TypeParamRef("T".into())),
2199                ]),
2200                TypeDef::Object(vec![
2201                    Field::new("ok", TypeDef::Literal(Literal::Boolean(false))),
2202                    Field::new("error", TypeDef::TypeParamRef("E".into())),
2203                ]),
2204            ])),
2205        };
2206        assert_eq!(
2207            result_def.render_declaration(),
2208            "type Result<T, E = Error> = { ok: true; value: T } | { ok: false; error: E };"
2209        );
2210    }
2211
2212    #[test]
2213    fn test_generic_application_with_def() {
2214        // Using Core<TextData> where TextData = { type: "text"; content: string }
2215        let application = TypeDef::Generic {
2216            base: "Core".into(),
2217            args: vec![TypeDef::Object(vec![
2218                Field::new("type", TypeDef::Literal(Literal::String("text".into()))),
2219                Field::new("content", TypeDef::Primitive(Primitive::String)),
2220            ])],
2221        };
2222        assert_eq!(
2223            application.render(),
2224            "Core<{ type: \"text\"; content: string }>"
2225        );
2226    }
2227
2228    #[test]
2229    fn test_registry_with_generic_def() {
2230        let mut registry = TypeRegistry::new();
2231
2232        // Add a generic type definition
2233        let core_def = TypeDef::GenericDef {
2234            name: "Core".into(),
2235            type_params: vec![TypeParam::new("T")],
2236            def: Box::new(TypeDef::Object(vec![
2237                Field::new("id", TypeDef::Primitive(Primitive::String)),
2238                Field::new("data", TypeDef::TypeParamRef("T".into())),
2239            ])),
2240        };
2241        registry.add_typedef(core_def);
2242
2243        assert_eq!(registry.len(), 1);
2244        assert!(registry.get("Core").is_some());
2245
2246        let output = registry.render();
2247        assert!(output.contains("type Core<T> = { id: string; data: T };"));
2248    }
2249
2250    #[test]
2251    fn test_registry_exported_with_generic_def() {
2252        let mut registry = TypeRegistry::new();
2253
2254        let core_def = TypeDef::GenericDef {
2255            name: "Core".into(),
2256            type_params: vec![TypeParam::new("T").with_constraint(TypeDef::Object(vec![
2257                Field::new("type", TypeDef::Primitive(Primitive::String)),
2258            ]))],
2259            def: Box::new(TypeDef::Object(vec![
2260                Field::new("data", TypeDef::TypeParamRef("T".into())),
2261            ])),
2262        };
2263        registry.add_typedef(core_def);
2264
2265        let output = registry.render_exported();
2266        assert!(output.contains("export type Core<T extends { type: string }> = { data: T };"));
2267    }
2268
2269    #[test]
2270    fn test_registry_generic_depends_on_constraint() {
2271        let mut registry = TypeRegistry::new();
2272
2273        // First, define the constraint type
2274        let discriminant = TypeDef::Named {
2275            namespace: vec![],
2276            name: "Discriminant".into(),
2277            def: Box::new(TypeDef::Object(vec![Field::new(
2278                "type",
2279                TypeDef::Primitive(Primitive::String),
2280            )])),
2281            module: None,
2282        };
2283
2284        // Then define a generic using that type as a constraint
2285        let core_def = TypeDef::GenericDef {
2286            name: "Core".into(),
2287            type_params: vec![TypeParam::new("T").with_constraint(TypeDef::Ref("Discriminant".into()))],
2288            def: Box::new(TypeDef::Object(vec![
2289                Field::new("data", TypeDef::TypeParamRef("T".into())),
2290            ])),
2291        };
2292
2293        registry.add_typedef(core_def);
2294        registry.add_typedef(discriminant);
2295
2296        let sorted = registry.sorted_types();
2297
2298        // Discriminant should come before Core
2299        let discrim_pos = sorted.iter().position(|&n| n == "Discriminant").unwrap();
2300        let core_pos = sorted.iter().position(|&n| n == "Core").unwrap();
2301        assert!(discrim_pos < core_pos, "Discriminant should come before Core");
2302    }
2303
2304    #[test]
2305    fn test_full_core_pattern_example() {
2306        // Full example of the Core<T> pattern for rich discriminated unions
2307        let mut registry = TypeRegistry::new();
2308
2309        // 1. Define the Core wrapper
2310        let core_def = TypeDef::GenericDef {
2311            name: "Core".into(),
2312            type_params: vec![TypeParam::new("T").with_constraint(TypeDef::Object(vec![
2313                Field::new("type", TypeDef::Primitive(Primitive::String)),
2314            ]))],
2315            def: Box::new(TypeDef::Object(vec![
2316                Field::new("id", TypeDef::Primitive(Primitive::String)),
2317                Field::new("timestamp", TypeDef::Primitive(Primitive::Number)),
2318                Field::new("data", TypeDef::TypeParamRef("T".into())),
2319            ])),
2320        };
2321
2322        // 2. Define variant types
2323        let text_data = TypeDef::Named {
2324            namespace: vec![],
2325            name: "TextData".into(),
2326            def: Box::new(TypeDef::Object(vec![
2327                Field::new("type", TypeDef::Literal(Literal::String("text".into()))),
2328                Field::new("content", TypeDef::Primitive(Primitive::String)),
2329            ])),
2330            module: None,
2331        };
2332
2333        let image_data = TypeDef::Named {
2334            namespace: vec![],
2335            name: "ImageData".into(),
2336            def: Box::new(TypeDef::Object(vec![
2337                Field::new("type", TypeDef::Literal(Literal::String("image".into()))),
2338                Field::new("url", TypeDef::Primitive(Primitive::String)),
2339            ])),
2340            module: None,
2341        };
2342
2343        // 3. Define wrapped message types
2344        let text_message = TypeDef::Named {
2345            namespace: vec![],
2346            name: "TextMessage".into(),
2347            def: Box::new(TypeDef::Generic {
2348                base: "Core".into(),
2349                args: vec![TypeDef::Ref("TextData".into())],
2350            }),
2351            module: None,
2352        };
2353
2354        let image_message = TypeDef::Named {
2355            namespace: vec![],
2356            name: "ImageMessage".into(),
2357            def: Box::new(TypeDef::Generic {
2358                base: "Core".into(),
2359                args: vec![TypeDef::Ref("ImageData".into())],
2360            }),
2361            module: None,
2362        };
2363
2364        // 4. Define the union type
2365        let message = TypeDef::Named {
2366            namespace: vec![],
2367            name: "Message".into(),
2368            def: Box::new(TypeDef::Union(vec![
2369                TypeDef::Ref("TextMessage".into()),
2370                TypeDef::Ref("ImageMessage".into()),
2371            ])),
2372            module: None,
2373        };
2374
2375        registry.add_typedef(core_def);
2376        registry.add_typedef(text_data);
2377        registry.add_typedef(image_data);
2378        registry.add_typedef(text_message);
2379        registry.add_typedef(image_message);
2380        registry.add_typedef(message);
2381
2382        let output = registry.render_exported();
2383
2384        // Verify all types are present
2385        assert!(output.contains("export type Core<T extends { type: string }>"));
2386        assert!(output.contains("export type TextData ="));
2387        assert!(output.contains("export type ImageData ="));
2388        assert!(output.contains("export type TextMessage = Core<TextData>"));
2389        assert!(output.contains("export type ImageMessage = Core<ImageData>"));
2390        assert!(output.contains("export type Message = TextMessage | ImageMessage"));
2391    }
2392}