ferro_type/
lib.rs

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