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