Skip to main content

metaxy_cli/codegen/
overrides.rs

1use std::collections::HashMap;
2
3use crate::model::{Manifest, RustType, VariantKind};
4
5/// Builds a base-name lookup index from type override keys.
6///
7/// Each key is normalized to its last path segment (e.g. `"chrono::DateTime"` → `"DateTime"`).
8/// This index is used as a fallback when an exact full-path match fails, which happens
9/// when the Rust source uses an imported name rather than a fully-qualified path.
10pub fn build_base_index(overrides: &HashMap<String, String>) -> HashMap<String, String> {
11    overrides
12        .iter()
13        .map(|(k, v)| {
14            let base = k.rsplit("::").next().unwrap_or(k);
15            (base.to_string(), v.clone())
16        })
17        .collect()
18}
19
20/// Applies type overrides to every [`RustType`] node in the manifest.
21///
22/// Matching order for each type node:
23/// 1. **Exact match** — the full `RustType.name` is looked up in `overrides`
24///    (handles fully-qualified paths like `chrono::DateTime`).
25/// 2. **Base-name fallback** — the last segment of `RustType.name` is looked up
26///    in `base_index` (handles imported names like `DateTime`).
27///
28/// When a match is found the node's name is replaced with the TypeScript type
29/// string and its generic parameters are cleared (e.g. `DateTime<Utc>` → `string`).
30pub fn apply_type_overrides(
31    manifest: &mut Manifest,
32    overrides: &HashMap<String, String>,
33    base_index: &HashMap<String, String>,
34) {
35    if overrides.is_empty() {
36        return;
37    }
38
39    for proc in &mut manifest.procedures {
40        if let Some(ty) = &mut proc.input {
41            override_type(ty, overrides, base_index);
42        }
43        if let Some(ty) = &mut proc.output {
44            override_type(ty, overrides, base_index);
45        }
46    }
47
48    for s in &mut manifest.structs {
49        for field in &mut s.fields {
50            override_type(&mut field.ty, overrides, base_index);
51        }
52        for ty in &mut s.tuple_fields {
53            override_type(ty, overrides, base_index);
54        }
55    }
56
57    for e in &mut manifest.enums {
58        for variant in &mut e.variants {
59            match &mut variant.kind {
60                VariantKind::Unit => {}
61                VariantKind::Tuple(types) => {
62                    for ty in types {
63                        override_type(ty, overrides, base_index);
64                    }
65                }
66                VariantKind::Struct(fields) => {
67                    for field in fields {
68                        override_type(&mut field.ty, overrides, base_index);
69                    }
70                }
71            }
72        }
73    }
74}
75
76/// Recursively overrides a single [`RustType`] node and its generic parameters.
77fn override_type(
78    ty: &mut RustType,
79    overrides: &HashMap<String, String>,
80    base_index: &HashMap<String, String>,
81) {
82    // Exact match on full name first, then base-name fallback
83    if let Some(ts_type) = overrides
84        .get(&ty.name)
85        .or_else(|| base_index.get(ty.base_name()))
86    {
87        ty.name = ts_type.clone();
88        ty.generics.clear();
89        return;
90    }
91
92    for g in &mut ty.generics {
93        override_type(g, overrides, base_index);
94    }
95}