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;
17
18use std::collections::HashMap;
19
20// ============================================================================
21// CORE TRAIT AND IR (TypeScript + TypeDef)
22// ============================================================================
23
24/// The core trait for types that can be represented as TypeScript.
25///
26/// This is the foundation trait for ferrotype, similar to how `Serialize` is
27/// the foundation of serde. Implementations return a [`TypeDef`] that describes
28/// the TypeScript representation of the type.
29///
30/// # Design Philosophy
31///
32/// Unlike string-based approaches, returning a structured [`TypeDef`] enables:
33/// - **Deduplication**: Named types can be collected and emitted once
34/// - **Analysis**: Dependencies between types can be tracked
35/// - **Flexibility**: The IR can be rendered in different styles
36/// - **Composition**: Complex types build from simpler TypeDefs
37///
38/// # Example
39///
40/// ```ignore
41/// use ferrotype::{TypeScript, TypeDef, Primitive};
42///
43/// struct UserId(String);
44///
45/// impl TypeScript for UserId {
46///     fn typescript() -> TypeDef {
47///         TypeDef::Named {
48///             name: "UserId".into(),
49///             def: Box::new(TypeDef::Primitive(Primitive::String)),
50///         }
51///     }
52/// }
53/// ```
54pub trait TypeScript {
55    /// Returns the TypeScript type definition for this type.
56    fn typescript() -> TypeDef;
57}
58
59/// Intermediate representation for TypeScript types.
60///
61/// This enum represents all TypeScript types that ferrotype can generate.
62/// It serves as the IR between Rust types and TypeScript output, enabling
63/// analysis and transformation before rendering.
64///
65/// # Type Categories
66///
67/// - **Primitives**: `string`, `number`, `boolean`, `null`, etc.
68/// - **Compounds**: Arrays, tuples, objects, unions, intersections
69/// - **References**: Named types and type references
70/// - **Literals**: Specific string, number, or boolean values
71#[derive(Debug, Clone, PartialEq)]
72pub enum TypeDef {
73    /// A primitive TypeScript type.
74    Primitive(Primitive),
75
76    /// An array type: `T[]`
77    Array(Box<TypeDef>),
78
79    /// A tuple type: `[T1, T2, ...]`
80    Tuple(Vec<TypeDef>),
81
82    /// An object type with named fields: `{ field1: T1; field2?: T2; }`
83    Object(Vec<Field>),
84
85    /// A union type: `T1 | T2 | ...`
86    Union(Vec<TypeDef>),
87
88    /// An intersection type: `T1 & T2 & ...`
89    Intersection(Vec<TypeDef>),
90
91    /// A record/dictionary type: `Record<K, V>` or `{ [key: K]: V }`
92    Record {
93        key: Box<TypeDef>,
94        value: Box<TypeDef>,
95    },
96
97    /// A named type definition that should be emitted as a separate declaration.
98    /// This is the primary mechanism for type deduplication.
99    Named {
100        name: String,
101        def: Box<TypeDef>,
102    },
103
104    /// A reference to a named type. Used to avoid infinite recursion and
105    /// to generate cleaner output by referencing previously-defined types.
106    Ref(String),
107
108    /// A literal type with a specific value.
109    Literal(Literal),
110
111    /// A function type: `(arg1: T1, arg2: T2) => R`
112    Function {
113        params: Vec<Field>,
114        return_type: Box<TypeDef>,
115    },
116
117    /// A generic type application: `Generic<T1, T2>`
118    Generic {
119        base: String,
120        args: Vec<TypeDef>,
121    },
122}
123
124/// Primitive TypeScript types.
125#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
126pub enum Primitive {
127    /// The `string` type.
128    String,
129    /// The `number` type.
130    Number,
131    /// The `boolean` type.
132    Boolean,
133    /// The `null` type.
134    Null,
135    /// The `undefined` type.
136    Undefined,
137    /// The `void` type (for functions that don't return a value).
138    Void,
139    /// The `never` type (for functions that never return).
140    Never,
141    /// The `any` type (escape hatch, use sparingly).
142    Any,
143    /// The `unknown` type (type-safe alternative to any).
144    Unknown,
145    /// The `bigint` type.
146    BigInt,
147}
148
149/// A field in an object type.
150#[derive(Debug, Clone, PartialEq)]
151pub struct Field {
152    /// The field name.
153    pub name: String,
154    /// The field's type.
155    pub ty: TypeDef,
156    /// Whether the field is optional (`field?: T` vs `field: T`).
157    pub optional: bool,
158    /// Whether the field is readonly.
159    pub readonly: bool,
160}
161
162impl Field {
163    /// Creates a new required field.
164    pub fn new(name: impl Into<String>, ty: TypeDef) -> Self {
165        Self {
166            name: name.into(),
167            ty,
168            optional: false,
169            readonly: false,
170        }
171    }
172
173    /// Creates a new optional field.
174    pub fn optional(name: impl Into<String>, ty: TypeDef) -> Self {
175        Self {
176            name: name.into(),
177            ty,
178            optional: true,
179            readonly: false,
180        }
181    }
182
183    /// Makes this field readonly.
184    pub fn readonly(mut self) -> Self {
185        self.readonly = true;
186        self
187    }
188}
189
190/// A literal TypeScript type with a specific value.
191#[derive(Debug, Clone, PartialEq)]
192pub enum Literal {
193    /// A string literal: `"foo"`
194    String(String),
195    /// A number literal: `42`
196    Number(f64),
197    /// A boolean literal: `true` or `false`
198    Boolean(bool),
199}
200
201impl TypeDef {
202    /// Renders this TypeDef to TypeScript syntax.
203    pub fn render(&self) -> String {
204        match self {
205            TypeDef::Primitive(p) => p.render().to_string(),
206            TypeDef::Array(inner) => {
207                let inner_str = inner.render();
208                // Wrap union types in parens for array syntax
209                if matches!(inner.as_ref(), TypeDef::Union(_)) {
210                    format!("({})[]", inner_str)
211                } else {
212                    format!("{}[]", inner_str)
213                }
214            }
215            TypeDef::Tuple(items) => {
216                let items_str: Vec<_> = items.iter().map(|t| t.render()).collect();
217                format!("[{}]", items_str.join(", "))
218            }
219            TypeDef::Object(fields) => {
220                if fields.is_empty() {
221                    "{}".to_string()
222                } else {
223                    let fields_str: Vec<_> = fields
224                        .iter()
225                        .map(|f| {
226                            let readonly = if f.readonly { "readonly " } else { "" };
227                            let opt = if f.optional { "?" } else { "" };
228                            format!("{}{}{}: {}", readonly, f.name, opt, f.ty.render())
229                        })
230                        .collect();
231                    format!("{{ {} }}", fields_str.join("; "))
232                }
233            }
234            TypeDef::Union(variants) => {
235                let variants_str: Vec<_> = variants.iter().map(|t| t.render()).collect();
236                variants_str.join(" | ")
237            }
238            TypeDef::Intersection(types) => {
239                let types_str: Vec<_> = types.iter().map(|t| t.render()).collect();
240                types_str.join(" & ")
241            }
242            TypeDef::Record { key, value } => {
243                format!("Record<{}, {}>", key.render(), value.render())
244            }
245            TypeDef::Named { name, .. } => name.clone(),
246            TypeDef::Ref(name) => name.clone(),
247            TypeDef::Literal(lit) => lit.render(),
248            TypeDef::Function {
249                params,
250                return_type,
251            } => {
252                let params_str: Vec<_> = params
253                    .iter()
254                    .map(|p| format!("{}: {}", p.name, p.ty.render()))
255                    .collect();
256                format!("({}) => {}", params_str.join(", "), return_type.render())
257            }
258            TypeDef::Generic { base, args } => {
259                let args_str: Vec<_> = args.iter().map(|t| t.render()).collect();
260                format!("{}<{}>", base, args_str.join(", "))
261            }
262        }
263    }
264
265    /// Renders a full type declaration for named types.
266    ///
267    /// For `Named` types, this returns `type Name = Definition;`
268    /// For other types, this just returns the rendered type.
269    pub fn render_declaration(&self) -> String {
270        match self {
271            TypeDef::Named { name, def } => {
272                format!("type {} = {};", name, def.render())
273            }
274            _ => self.render(),
275        }
276    }
277}
278
279impl Primitive {
280    /// Renders this primitive to its TypeScript keyword.
281    pub fn render(&self) -> &'static str {
282        match self {
283            Primitive::String => "string",
284            Primitive::Number => "number",
285            Primitive::Boolean => "boolean",
286            Primitive::Null => "null",
287            Primitive::Undefined => "undefined",
288            Primitive::Void => "void",
289            Primitive::Never => "never",
290            Primitive::Any => "any",
291            Primitive::Unknown => "unknown",
292            Primitive::BigInt => "bigint",
293        }
294    }
295}
296
297impl Literal {
298    /// Renders this literal to TypeScript syntax.
299    pub fn render(&self) -> String {
300        match self {
301            Literal::String(s) => format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\"")),
302            Literal::Number(n) => {
303                if n.fract() == 0.0 {
304                    format!("{}", *n as i64)
305                } else {
306                    format!("{}", n)
307                }
308            }
309            Literal::Boolean(b) => b.to_string(),
310        }
311    }
312}
313
314// ============================================================================
315// HELPER FUNCTIONS
316// ============================================================================
317
318/// Extracts fields from an Object TypeDef, unwrapping Named if necessary.
319///
320/// This is used by the derive macro to implement `#[ts(flatten)]`. When a field
321/// is marked with flatten, its type's fields are extracted and merged into the
322/// containing object.
323///
324/// # Panics
325///
326/// Panics if the TypeDef is not an Object (or Named wrapping an Object).
327pub fn extract_object_fields(typedef: &TypeDef) -> Vec<Field> {
328    match typedef {
329        TypeDef::Object(fields) => fields.clone(),
330        TypeDef::Named { def, .. } => extract_object_fields(def),
331        other => panic!(
332            "#[ts(flatten)] can only be used on fields with object types, got: {:?}",
333            other
334        ),
335    }
336}
337
338/// Extracts the inner type definition, unwrapping Named if necessary.
339///
340/// This is used by the derive macro to implement `#[ts(inline)]`. When a field
341/// is marked with inline, the full type definition is inlined instead of using
342/// a type reference.
343pub fn inline_typedef(typedef: TypeDef) -> TypeDef {
344    match typedef {
345        TypeDef::Named { def, .. } => *def,
346        other => other,
347    }
348}
349
350// ============================================================================
351// TYPE REGISTRY
352// ============================================================================
353
354use std::collections::{HashSet, VecDeque};
355
356/// A registry for collecting and managing TypeScript type definitions.
357///
358/// The TypeRegistry collects named types, resolves their dependency order,
359/// and renders them to a TypeScript file. This enables:
360/// - **Deduplication**: Each named type is emitted once
361/// - **Ordering**: Types are emitted in dependency order
362/// - **Output**: Generate valid .ts or .d.ts files
363///
364/// # Example
365///
366/// ```ignore
367/// use ferrotype::{TypeRegistry, TypeScript};
368///
369/// let mut registry = TypeRegistry::new();
370/// registry.register::<User>();
371/// registry.register::<Post>();
372///
373/// let output = registry.render();
374/// std::fs::write("types.ts", output)?;
375/// ```
376#[derive(Debug, Default)]
377pub struct TypeRegistry {
378    /// Named type definitions, keyed by name
379    types: HashMap<String, TypeDef>,
380    /// Order in which types were registered (for stable output when no deps)
381    registration_order: Vec<String>,
382}
383
384impl TypeRegistry {
385    /// Creates a new empty registry.
386    pub fn new() -> Self {
387        Self::default()
388    }
389
390    /// Registers a type that implements TypeScript.
391    ///
392    /// This extracts all named types from the type definition and adds them
393    /// to the registry. Named types are deduplicated by name.
394    pub fn register<T: TypeScript>(&mut self) {
395        let typedef = T::typescript();
396        self.add_typedef(typedef);
397    }
398
399    /// Adds a TypeDef to the registry, extracting all named types.
400    pub fn add_typedef(&mut self, typedef: TypeDef) {
401        self.extract_named_types(&typedef);
402    }
403
404    /// Recursively extracts all Named types from a TypeDef.
405    fn extract_named_types(&mut self, typedef: &TypeDef) {
406        match typedef {
407            TypeDef::Named { name, def } => {
408                if !self.types.contains_key(name) {
409                    self.types.insert(name.clone(), typedef.clone());
410                    self.registration_order.push(name.clone());
411                    // Also extract from the inner definition
412                    self.extract_named_types(def);
413                }
414            }
415            TypeDef::Array(inner) => self.extract_named_types(inner),
416            TypeDef::Tuple(items) => {
417                for item in items {
418                    self.extract_named_types(item);
419                }
420            }
421            TypeDef::Object(fields) => {
422                for field in fields {
423                    self.extract_named_types(&field.ty);
424                }
425            }
426            TypeDef::Union(items) | TypeDef::Intersection(items) => {
427                for item in items {
428                    self.extract_named_types(item);
429                }
430            }
431            TypeDef::Record { key, value } => {
432                self.extract_named_types(key);
433                self.extract_named_types(value);
434            }
435            TypeDef::Function { params, return_type } => {
436                for param in params {
437                    self.extract_named_types(&param.ty);
438                }
439                self.extract_named_types(return_type);
440            }
441            TypeDef::Generic { args, .. } => {
442                for arg in args {
443                    self.extract_named_types(arg);
444                }
445            }
446            // Primitives, Refs, and Literals have no nested named types
447            TypeDef::Primitive(_) | TypeDef::Ref(_) | TypeDef::Literal(_) => {}
448        }
449    }
450
451    /// Returns the number of registered types.
452    pub fn len(&self) -> usize {
453        self.types.len()
454    }
455
456    /// Returns true if no types are registered.
457    pub fn is_empty(&self) -> bool {
458        self.types.is_empty()
459    }
460
461    /// Returns the names of all registered types.
462    pub fn type_names(&self) -> impl Iterator<Item = &str> {
463        self.types.keys().map(|s| s.as_str())
464    }
465
466    /// Gets a type definition by name.
467    pub fn get(&self, name: &str) -> Option<&TypeDef> {
468        self.types.get(name)
469    }
470
471    /// Computes the dependencies for a type (what other named types it references).
472    fn get_dependencies(&self, typedef: &TypeDef) -> HashSet<String> {
473        let mut deps = HashSet::new();
474        self.collect_dependencies(typedef, &mut deps);
475        deps
476    }
477
478    /// Recursively collects dependencies from a TypeDef.
479    fn collect_dependencies(&self, typedef: &TypeDef, deps: &mut HashSet<String>) {
480        match typedef {
481            TypeDef::Named { def, .. } => {
482                // Don't add self as dependency, but check inner def
483                self.collect_dependencies(def, deps);
484            }
485            TypeDef::Ref(name) => {
486                if self.types.contains_key(name) {
487                    deps.insert(name.clone());
488                }
489            }
490            TypeDef::Array(inner) => self.collect_dependencies(inner, deps),
491            TypeDef::Tuple(items) => {
492                for item in items {
493                    self.collect_dependencies(item, deps);
494                }
495            }
496            TypeDef::Object(fields) => {
497                for field in fields {
498                    self.collect_dependencies(&field.ty, deps);
499                }
500            }
501            TypeDef::Union(variants) => {
502                for v in variants {
503                    self.collect_dependencies(v, deps);
504                }
505            }
506            TypeDef::Intersection(types) => {
507                for t in types {
508                    self.collect_dependencies(t, deps);
509                }
510            }
511            TypeDef::Record { key, value } => {
512                self.collect_dependencies(key, deps);
513                self.collect_dependencies(value, deps);
514            }
515            TypeDef::Function { params, return_type } => {
516                for param in params {
517                    self.collect_dependencies(&param.ty, deps);
518                }
519                self.collect_dependencies(return_type, deps);
520            }
521            TypeDef::Generic { args, .. } => {
522                for arg in args {
523                    self.collect_dependencies(arg, deps);
524                }
525            }
526            TypeDef::Primitive(_) | TypeDef::Literal(_) => {}
527        }
528    }
529
530    /// Returns types in dependency order (types with no dependencies first).
531    ///
532    /// Uses Kahn's algorithm for topological sort.
533    pub fn sorted_types(&self) -> Vec<&str> {
534        // Build dependency graph
535        let mut in_degree: HashMap<&str, usize> = HashMap::new();
536        let mut dependents: HashMap<&str, Vec<&str>> = HashMap::new();
537
538        // Initialize
539        for name in self.types.keys() {
540            in_degree.insert(name.as_str(), 0);
541            dependents.insert(name.as_str(), Vec::new());
542        }
543
544        // Calculate in-degrees and build reverse graph
545        for (name, typedef) in &self.types {
546            let deps = self.get_dependencies(typedef);
547            for dep in deps {
548                if let Some(dep_name) = self.types.get_key_value(&dep) {
549                    *in_degree.get_mut(name.as_str()).unwrap() += 1;
550                    dependents.get_mut(dep_name.0.as_str()).unwrap().push(name.as_str());
551                }
552            }
553        }
554
555        // Kahn's algorithm
556        let mut queue: VecDeque<&str> = VecDeque::new();
557        let mut result: Vec<&str> = Vec::new();
558
559        // Start with types that have no dependencies
560        for (name, &degree) in &in_degree {
561            if degree == 0 {
562                queue.push_back(name);
563            }
564        }
565
566        // Sort the initial queue by registration order for stable output
567        let mut initial: Vec<_> = queue.drain(..).collect();
568        initial.sort_by_key(|name| {
569            self.registration_order.iter().position(|n| n == *name).unwrap_or(usize::MAX)
570        });
571        queue.extend(initial);
572
573        while let Some(name) = queue.pop_front() {
574            result.push(name);
575
576            // Get dependents sorted by registration order for stable output
577            let mut deps: Vec<_> = dependents.get(name).map(|v| v.as_slice()).unwrap_or(&[]).to_vec();
578            deps.sort_by_key(|n| {
579                self.registration_order.iter().position(|name| name == *n).unwrap_or(usize::MAX)
580            });
581
582            for dependent in deps {
583                let degree = in_degree.get_mut(dependent).unwrap();
584                *degree -= 1;
585                if *degree == 0 {
586                    queue.push_back(dependent);
587                }
588            }
589        }
590
591        // If result doesn't contain all types, there's a cycle
592        // Fall back to registration order for remaining types
593        if result.len() < self.types.len() {
594            for name in &self.registration_order {
595                if !result.contains(&name.as_str()) {
596                    result.push(name.as_str());
597                }
598            }
599        }
600
601        result
602    }
603
604    /// Renders all registered types to TypeScript declarations.
605    ///
606    /// Types are emitted in dependency order, with proper formatting.
607    pub fn render(&self) -> String {
608        let sorted = self.sorted_types();
609        let mut output = String::new();
610
611        // Add header comment
612        output.push_str("// Generated by ferrotype\n");
613        output.push_str("// Do not edit manually\n\n");
614
615        for name in sorted {
616            if let Some(typedef) = self.types.get(name) {
617                output.push_str(&typedef.render_declaration());
618                output.push_str("\n\n");
619            }
620        }
621
622        // Remove trailing newline
623        output.trim_end().to_string() + "\n"
624    }
625
626    /// Renders all registered types with `export` keywords.
627    pub fn render_exported(&self) -> String {
628        let sorted = self.sorted_types();
629        let mut output = String::new();
630
631        // Add header comment
632        output.push_str("// Generated by ferrotype\n");
633        output.push_str("// Do not edit manually\n\n");
634
635        for name in sorted {
636            if let Some(typedef) = self.types.get(name) {
637                if let TypeDef::Named { name, def } = typedef {
638                    output.push_str(&format!("export type {} = {};\n\n", name, def.render()));
639                }
640            }
641        }
642
643        // Remove trailing newline
644        output.trim_end().to_string() + "\n"
645    }
646
647    /// Clears all registered types.
648    pub fn clear(&mut self) {
649        self.types.clear();
650        self.registration_order.clear();
651    }
652}
653
654// ============================================================================
655// TypeScript TRAIT IMPLEMENTATIONS FOR PRIMITIVES
656// ============================================================================
657
658impl TypeScript for () {
659    fn typescript() -> TypeDef {
660        TypeDef::Primitive(Primitive::Void)
661    }
662}
663
664impl TypeScript for bool {
665    fn typescript() -> TypeDef {
666        TypeDef::Primitive(Primitive::Boolean)
667    }
668}
669
670impl TypeScript for String {
671    fn typescript() -> TypeDef {
672        TypeDef::Primitive(Primitive::String)
673    }
674}
675
676impl TypeScript for &str {
677    fn typescript() -> TypeDef {
678        TypeDef::Primitive(Primitive::String)
679    }
680}
681
682impl TypeScript for char {
683    fn typescript() -> TypeDef {
684        TypeDef::Primitive(Primitive::String)
685    }
686}
687
688macro_rules! impl_typescript_number {
689    ($($t:ty),*) => {
690        $(
691            impl TypeScript for $t {
692                fn typescript() -> TypeDef {
693                    TypeDef::Primitive(Primitive::Number)
694                }
695            }
696        )*
697    };
698}
699
700impl_typescript_number!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64);
701
702// i128/u128 map to bigint in TypeScript
703impl TypeScript for i128 {
704    fn typescript() -> TypeDef {
705        TypeDef::Primitive(Primitive::BigInt)
706    }
707}
708
709impl TypeScript for u128 {
710    fn typescript() -> TypeDef {
711        TypeDef::Primitive(Primitive::BigInt)
712    }
713}
714
715// ============================================================================
716// TypeScript TRAIT IMPLEMENTATIONS FOR GENERIC TYPES
717// ============================================================================
718
719impl<T: TypeScript> TypeScript for Option<T> {
720    fn typescript() -> TypeDef {
721        TypeDef::Union(vec![T::typescript(), TypeDef::Primitive(Primitive::Null)])
722    }
723}
724
725impl<T: TypeScript> TypeScript for Vec<T> {
726    fn typescript() -> TypeDef {
727        TypeDef::Array(Box::new(T::typescript()))
728    }
729}
730
731impl<T: TypeScript> TypeScript for Box<T> {
732    fn typescript() -> TypeDef {
733        T::typescript()
734    }
735}
736
737impl<T: TypeScript> TypeScript for std::rc::Rc<T> {
738    fn typescript() -> TypeDef {
739        T::typescript()
740    }
741}
742
743impl<T: TypeScript> TypeScript for std::sync::Arc<T> {
744    fn typescript() -> TypeDef {
745        T::typescript()
746    }
747}
748
749impl<T: TypeScript> TypeScript for std::cell::RefCell<T> {
750    fn typescript() -> TypeDef {
751        T::typescript()
752    }
753}
754
755impl<T: TypeScript> TypeScript for std::cell::Cell<T> {
756    fn typescript() -> TypeDef {
757        T::typescript()
758    }
759}
760
761impl<K: TypeScript, V: TypeScript> TypeScript for HashMap<K, V> {
762    fn typescript() -> TypeDef {
763        TypeDef::Record {
764            key: Box::new(K::typescript()),
765            value: Box::new(V::typescript()),
766        }
767    }
768}
769
770impl<K: TypeScript, V: TypeScript> TypeScript for std::collections::BTreeMap<K, V> {
771    fn typescript() -> TypeDef {
772        TypeDef::Record {
773            key: Box::new(K::typescript()),
774            value: Box::new(V::typescript()),
775        }
776    }
777}
778
779impl<T: TypeScript, E: TypeScript> TypeScript for Result<T, E> {
780    fn typescript() -> TypeDef {
781        TypeDef::Union(vec![
782            TypeDef::Object(vec![
783                Field::new("ok", TypeDef::Literal(Literal::Boolean(true))),
784                Field::new("value", T::typescript()),
785            ]),
786            TypeDef::Object(vec![
787                Field::new("ok", TypeDef::Literal(Literal::Boolean(false))),
788                Field::new("error", E::typescript()),
789            ]),
790        ])
791    }
792}
793
794// ============================================================================
795// TypeScript TRAIT IMPLEMENTATIONS FOR TUPLES
796// ============================================================================
797
798impl<A: TypeScript> TypeScript for (A,) {
799    fn typescript() -> TypeDef {
800        TypeDef::Tuple(vec![A::typescript()])
801    }
802}
803
804impl<A: TypeScript, B: TypeScript> TypeScript for (A, B) {
805    fn typescript() -> TypeDef {
806        TypeDef::Tuple(vec![A::typescript(), B::typescript()])
807    }
808}
809
810impl<A: TypeScript, B: TypeScript, C: TypeScript> TypeScript for (A, B, C) {
811    fn typescript() -> TypeDef {
812        TypeDef::Tuple(vec![A::typescript(), B::typescript(), C::typescript()])
813    }
814}
815
816impl<A: TypeScript, B: TypeScript, C: TypeScript, D: TypeScript> TypeScript for (A, B, C, D) {
817    fn typescript() -> TypeDef {
818        TypeDef::Tuple(vec![
819            A::typescript(),
820            B::typescript(),
821            C::typescript(),
822            D::typescript(),
823        ])
824    }
825}
826
827impl<A: TypeScript, B: TypeScript, C: TypeScript, D: TypeScript, E: TypeScript> TypeScript
828    for (A, B, C, D, E)
829{
830    fn typescript() -> TypeDef {
831        TypeDef::Tuple(vec![
832            A::typescript(),
833            B::typescript(),
834            C::typescript(),
835            D::typescript(),
836            E::typescript(),
837        ])
838    }
839}
840
841impl<A: TypeScript, B: TypeScript, C: TypeScript, D: TypeScript, E: TypeScript, F: TypeScript>
842    TypeScript for (A, B, C, D, E, F)
843{
844    fn typescript() -> TypeDef {
845        TypeDef::Tuple(vec![
846            A::typescript(),
847            B::typescript(),
848            C::typescript(),
849            D::typescript(),
850            E::typescript(),
851            F::typescript(),
852        ])
853    }
854}
855
856// ============================================================================
857// TESTS
858// ============================================================================
859
860#[cfg(test)]
861mod tests {
862    use super::*;
863
864    // ========================================================================
865    // TypeScript TRAIT + TypeDef TESTS
866    // ========================================================================
867
868    #[test]
869    fn test_typedef_primitive_render() {
870        assert_eq!(TypeDef::Primitive(Primitive::String).render(), "string");
871        assert_eq!(TypeDef::Primitive(Primitive::Number).render(), "number");
872        assert_eq!(TypeDef::Primitive(Primitive::Boolean).render(), "boolean");
873        assert_eq!(TypeDef::Primitive(Primitive::Null).render(), "null");
874        assert_eq!(TypeDef::Primitive(Primitive::Undefined).render(), "undefined");
875        assert_eq!(TypeDef::Primitive(Primitive::Void).render(), "void");
876        assert_eq!(TypeDef::Primitive(Primitive::Never).render(), "never");
877        assert_eq!(TypeDef::Primitive(Primitive::Any).render(), "any");
878        assert_eq!(TypeDef::Primitive(Primitive::Unknown).render(), "unknown");
879        assert_eq!(TypeDef::Primitive(Primitive::BigInt).render(), "bigint");
880    }
881
882    #[test]
883    fn test_typedef_array_render() {
884        let arr = TypeDef::Array(Box::new(TypeDef::Primitive(Primitive::String)));
885        assert_eq!(arr.render(), "string[]");
886
887        // Union in array should be wrapped in parens
888        let union_arr = TypeDef::Array(Box::new(TypeDef::Union(vec![
889            TypeDef::Primitive(Primitive::String),
890            TypeDef::Primitive(Primitive::Number),
891        ])));
892        assert_eq!(union_arr.render(), "(string | number)[]");
893    }
894
895    #[test]
896    fn test_typedef_tuple_render() {
897        let tuple = TypeDef::Tuple(vec![
898            TypeDef::Primitive(Primitive::String),
899            TypeDef::Primitive(Primitive::Number),
900        ]);
901        assert_eq!(tuple.render(), "[string, number]");
902    }
903
904    #[test]
905    fn test_typedef_object_render() {
906        let obj = TypeDef::Object(vec![
907            Field::new("name", TypeDef::Primitive(Primitive::String)),
908            Field::optional("age", TypeDef::Primitive(Primitive::Number)),
909        ]);
910        assert_eq!(obj.render(), "{ name: string; age?: number }");
911
912        let empty_obj = TypeDef::Object(vec![]);
913        assert_eq!(empty_obj.render(), "{}");
914    }
915
916    #[test]
917    fn test_typedef_object_readonly_field() {
918        let obj = TypeDef::Object(vec![
919            Field::new("id", TypeDef::Primitive(Primitive::String)).readonly(),
920        ]);
921        assert_eq!(obj.render(), "{ readonly id: string }");
922    }
923
924    #[test]
925    fn test_typedef_union_render() {
926        let union = TypeDef::Union(vec![
927            TypeDef::Primitive(Primitive::String),
928            TypeDef::Primitive(Primitive::Number),
929            TypeDef::Primitive(Primitive::Null),
930        ]);
931        assert_eq!(union.render(), "string | number | null");
932    }
933
934    #[test]
935    fn test_typedef_intersection_render() {
936        let intersection = TypeDef::Intersection(vec![
937            TypeDef::Ref("Base".into()),
938            TypeDef::Object(vec![
939                Field::new("extra", TypeDef::Primitive(Primitive::String)),
940            ]),
941        ]);
942        assert_eq!(intersection.render(), "Base & { extra: string }");
943    }
944
945    #[test]
946    fn test_typedef_record_render() {
947        let record = TypeDef::Record {
948            key: Box::new(TypeDef::Primitive(Primitive::String)),
949            value: Box::new(TypeDef::Primitive(Primitive::Number)),
950        };
951        assert_eq!(record.render(), "Record<string, number>");
952    }
953
954    #[test]
955    fn test_typedef_named_render() {
956        let named = TypeDef::Named {
957            name: "UserId".into(),
958            def: Box::new(TypeDef::Primitive(Primitive::String)),
959        };
960        // Named types render as just their name (for inline use)
961        assert_eq!(named.render(), "UserId");
962        // Full declaration uses render_declaration
963        assert_eq!(named.render_declaration(), "type UserId = string;");
964    }
965
966    #[test]
967    fn test_typedef_ref_render() {
968        let ref_type = TypeDef::Ref("User".into());
969        assert_eq!(ref_type.render(), "User");
970    }
971
972    #[test]
973    fn test_typedef_literal_render() {
974        assert_eq!(TypeDef::Literal(Literal::String("foo".into())).render(), "\"foo\"");
975        assert_eq!(TypeDef::Literal(Literal::Number(42.0)).render(), "42");
976        assert_eq!(TypeDef::Literal(Literal::Number(3.14)).render(), "3.14");
977        assert_eq!(TypeDef::Literal(Literal::Boolean(true)).render(), "true");
978        assert_eq!(TypeDef::Literal(Literal::Boolean(false)).render(), "false");
979    }
980
981    #[test]
982    fn test_typedef_literal_escaping() {
983        let lit = Literal::String("say \"hello\"".into());
984        assert_eq!(lit.render(), "\"say \\\"hello\\\"\"");
985    }
986
987    #[test]
988    fn test_typedef_function_render() {
989        let func = TypeDef::Function {
990            params: vec![
991                Field::new("name", TypeDef::Primitive(Primitive::String)),
992                Field::new("age", TypeDef::Primitive(Primitive::Number)),
993            ],
994            return_type: Box::new(TypeDef::Primitive(Primitive::Void)),
995        };
996        assert_eq!(func.render(), "(name: string, age: number) => void");
997    }
998
999    #[test]
1000    fn test_typedef_generic_render() {
1001        let generic = TypeDef::Generic {
1002            base: "Promise".into(),
1003            args: vec![TypeDef::Primitive(Primitive::String)],
1004        };
1005        assert_eq!(generic.render(), "Promise<string>");
1006
1007        let multi_generic = TypeDef::Generic {
1008            base: "Map".into(),
1009            args: vec![
1010                TypeDef::Primitive(Primitive::String),
1011                TypeDef::Primitive(Primitive::Number),
1012            ],
1013        };
1014        assert_eq!(multi_generic.render(), "Map<string, number>");
1015    }
1016
1017    #[test]
1018    fn test_typescript_trait_primitives() {
1019        assert_eq!(<()>::typescript().render(), "void");
1020        assert_eq!(bool::typescript().render(), "boolean");
1021        assert_eq!(String::typescript().render(), "string");
1022        assert_eq!(i32::typescript().render(), "number");
1023        assert_eq!(f64::typescript().render(), "number");
1024        assert_eq!(i128::typescript().render(), "bigint");
1025        assert_eq!(u128::typescript().render(), "bigint");
1026    }
1027
1028    #[test]
1029    fn test_typescript_trait_option() {
1030        let opt = <Option<String>>::typescript();
1031        assert_eq!(opt.render(), "string | null");
1032    }
1033
1034    #[test]
1035    fn test_typescript_trait_vec() {
1036        let vec_type = <Vec<i32>>::typescript();
1037        assert_eq!(vec_type.render(), "number[]");
1038    }
1039
1040    #[test]
1041    fn test_typescript_trait_hashmap() {
1042        let map = <HashMap<String, i32>>::typescript();
1043        assert_eq!(map.render(), "Record<string, number>");
1044    }
1045
1046    #[test]
1047    fn test_typescript_trait_result() {
1048        let result = <Result<String, String>>::typescript();
1049        assert_eq!(
1050            result.render(),
1051            "{ ok: true; value: string } | { ok: false; error: string }"
1052        );
1053    }
1054
1055    #[test]
1056    fn test_typescript_trait_tuples() {
1057        assert_eq!(<(String,)>::typescript().render(), "[string]");
1058        assert_eq!(<(String, i32)>::typescript().render(), "[string, number]");
1059        assert_eq!(
1060            <(String, i32, bool)>::typescript().render(),
1061            "[string, number, boolean]"
1062        );
1063    }
1064
1065    #[test]
1066    fn test_typescript_trait_box() {
1067        // Box<T> should be transparent
1068        assert_eq!(<Box<String>>::typescript().render(), "string");
1069    }
1070
1071    #[test]
1072    fn test_typedef_equality() {
1073        let a = TypeDef::Primitive(Primitive::String);
1074        let b = TypeDef::Primitive(Primitive::String);
1075        let c = TypeDef::Primitive(Primitive::Number);
1076        assert_eq!(a, b);
1077        assert_ne!(a, c);
1078    }
1079
1080    #[test]
1081    fn test_field_builder() {
1082        let field = Field::new("name", TypeDef::Primitive(Primitive::String));
1083        assert!(!field.optional);
1084        assert!(!field.readonly);
1085
1086        let opt_field = Field::optional("name", TypeDef::Primitive(Primitive::String));
1087        assert!(opt_field.optional);
1088
1089        let readonly_field = Field::new("id", TypeDef::Primitive(Primitive::String)).readonly();
1090        assert!(readonly_field.readonly);
1091    }
1092
1093    // ========================================================================
1094    // TYPE REGISTRY TESTS
1095    // ========================================================================
1096
1097    #[test]
1098    fn test_registry_new() {
1099        let registry = TypeRegistry::new();
1100        assert!(registry.is_empty());
1101        assert_eq!(registry.len(), 0);
1102    }
1103
1104    #[test]
1105    fn test_registry_add_typedef() {
1106        let mut registry = TypeRegistry::new();
1107
1108        let user_type = TypeDef::Named {
1109            name: "User".to_string(),
1110            def: Box::new(TypeDef::Object(vec![
1111                Field::new("id", TypeDef::Primitive(Primitive::String)),
1112                Field::new("name", TypeDef::Primitive(Primitive::String)),
1113            ])),
1114        };
1115
1116        registry.add_typedef(user_type);
1117
1118        assert_eq!(registry.len(), 1);
1119        assert!(registry.get("User").is_some());
1120    }
1121
1122    #[test]
1123    fn test_registry_deduplication() {
1124        let mut registry = TypeRegistry::new();
1125
1126        let user_type = TypeDef::Named {
1127            name: "User".to_string(),
1128            def: Box::new(TypeDef::Primitive(Primitive::String)),
1129        };
1130
1131        registry.add_typedef(user_type.clone());
1132        registry.add_typedef(user_type);
1133
1134        // Should only have one User type
1135        assert_eq!(registry.len(), 1);
1136    }
1137
1138    #[test]
1139    fn test_registry_extracts_nested_types() {
1140        let mut registry = TypeRegistry::new();
1141
1142        // UserId type (no dependencies)
1143        let user_id = TypeDef::Named {
1144            name: "UserId".to_string(),
1145            def: Box::new(TypeDef::Primitive(Primitive::String)),
1146        };
1147
1148        // User type depends on UserId via Ref
1149        let user = TypeDef::Named {
1150            name: "User".to_string(),
1151            def: Box::new(TypeDef::Object(vec![
1152                Field::new("id", TypeDef::Ref("UserId".to_string())),
1153                Field::new("name", TypeDef::Primitive(Primitive::String)),
1154            ])),
1155        };
1156
1157        // Post type that references User type
1158        let post_type = TypeDef::Named {
1159            name: "Post".to_string(),
1160            def: Box::new(TypeDef::Object(vec![
1161                Field::new("title", TypeDef::Primitive(Primitive::String)),
1162                Field::new("author", user),
1163            ])),
1164        };
1165
1166        registry.add_typedef(post_type);
1167        registry.add_typedef(user_id);
1168
1169        // Should have Post, User, and UserId
1170        assert_eq!(registry.len(), 3);
1171        assert!(registry.get("Post").is_some());
1172        assert!(registry.get("User").is_some());
1173        assert!(registry.get("UserId").is_some());
1174    }
1175
1176    #[test]
1177    fn test_registry_render() {
1178        let mut registry = TypeRegistry::new();
1179
1180        let user_type = TypeDef::Named {
1181            name: "User".to_string(),
1182            def: Box::new(TypeDef::Object(vec![
1183                Field::new("id", TypeDef::Primitive(Primitive::String)),
1184                Field::new("name", TypeDef::Primitive(Primitive::String)),
1185            ])),
1186        };
1187
1188        registry.add_typedef(user_type);
1189
1190        let output = registry.render();
1191        assert!(output.contains("// Generated by ferrotype"));
1192        assert!(output.contains("type User = { id: string; name: string };"));
1193    }
1194
1195    #[test]
1196    fn test_registry_render_exported() {
1197        let mut registry = TypeRegistry::new();
1198
1199        let user_type = TypeDef::Named {
1200            name: "User".to_string(),
1201            def: Box::new(TypeDef::Primitive(Primitive::String)),
1202        };
1203
1204        registry.add_typedef(user_type);
1205
1206        let output = registry.render_exported();
1207        assert!(output.contains("export type User = string;"));
1208    }
1209
1210    #[test]
1211    fn test_registry_dependency_order() {
1212        let mut registry = TypeRegistry::new();
1213
1214        // UserId type (no dependencies)
1215        let user_id = TypeDef::Named {
1216            name: "UserId".to_string(),
1217            def: Box::new(TypeDef::Primitive(Primitive::String)),
1218        };
1219
1220        // User type depends on UserId via Ref
1221        let user = TypeDef::Named {
1222            name: "User".to_string(),
1223            def: Box::new(TypeDef::Object(vec![
1224                Field::new("id", TypeDef::Ref("UserId".to_string())),
1225                Field::new("name", TypeDef::Primitive(Primitive::String)),
1226            ])),
1227        };
1228
1229        // Add in reverse order (User before UserId)
1230        registry.add_typedef(user);
1231        registry.add_typedef(user_id);
1232
1233        let sorted = registry.sorted_types();
1234
1235        // UserId should come before User
1236        let user_id_pos = sorted.iter().position(|&n| n == "UserId").unwrap();
1237        let user_pos = sorted.iter().position(|&n| n == "User").unwrap();
1238        assert!(user_id_pos < user_pos, "UserId should come before User");
1239    }
1240
1241    #[test]
1242    fn test_registry_clear() {
1243        let mut registry = TypeRegistry::new();
1244
1245        let user_type = TypeDef::Named {
1246            name: "User".to_string(),
1247            def: Box::new(TypeDef::Primitive(Primitive::String)),
1248        };
1249
1250        registry.add_typedef(user_type);
1251        assert_eq!(registry.len(), 1);
1252
1253        registry.clear();
1254        assert!(registry.is_empty());
1255    }
1256
1257    #[test]
1258    fn test_registry_type_names() {
1259        let mut registry = TypeRegistry::new();
1260
1261        registry.add_typedef(TypeDef::Named {
1262            name: "Alpha".to_string(),
1263            def: Box::new(TypeDef::Primitive(Primitive::String)),
1264        });
1265        registry.add_typedef(TypeDef::Named {
1266            name: "Beta".to_string(),
1267            def: Box::new(TypeDef::Primitive(Primitive::Number)),
1268        });
1269
1270        let names: Vec<_> = registry.type_names().collect();
1271        assert_eq!(names.len(), 2);
1272        assert!(names.contains(&"Alpha"));
1273        assert!(names.contains(&"Beta"));
1274    }
1275
1276    #[test]
1277    fn test_registry_complex_dependencies() {
1278        let mut registry = TypeRegistry::new();
1279
1280        // A -> B -> C (C should come first)
1281        let c = TypeDef::Named {
1282            name: "C".to_string(),
1283            def: Box::new(TypeDef::Primitive(Primitive::String)),
1284        };
1285
1286        let b = TypeDef::Named {
1287            name: "B".to_string(),
1288            def: Box::new(TypeDef::Object(vec![
1289                Field::new("c", TypeDef::Ref("C".to_string())),
1290            ])),
1291        };
1292
1293        let a = TypeDef::Named {
1294            name: "A".to_string(),
1295            def: Box::new(TypeDef::Object(vec![
1296                Field::new("b", TypeDef::Ref("B".to_string())),
1297            ])),
1298        };
1299
1300        // Add in wrong order
1301        registry.add_typedef(a);
1302        registry.add_typedef(b);
1303        registry.add_typedef(c);
1304
1305        let sorted = registry.sorted_types();
1306
1307        let c_pos = sorted.iter().position(|&n| n == "C").unwrap();
1308        let b_pos = sorted.iter().position(|&n| n == "B").unwrap();
1309        let a_pos = sorted.iter().position(|&n| n == "A").unwrap();
1310
1311        assert!(c_pos < b_pos, "C should come before B");
1312        assert!(b_pos < a_pos, "B should come before A");
1313    }
1314}