ferro_type/
lib.rs

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