baobao_codegen/builder/
types.rs

1//! Language-agnostic type system for code generation.
2//!
3//! This module provides abstractions for representing types in a way that
4//! can be rendered to any target language via the [`TypeMapper`] trait.
5
6/// A language-agnostic type reference.
7///
8/// Types are represented semantically and can be rendered differently
9/// per target language.
10#[derive(Debug, Clone, PartialEq, Eq)]
11pub enum TypeRef {
12    /// A primitive type (string, int, bool, etc.).
13    Primitive(PrimitiveType),
14    /// An optional/nullable type.
15    Optional(Box<TypeRef>),
16    /// An array/list/vector type.
17    Array(Box<TypeRef>),
18    /// A named type (custom struct, class, interface, etc.).
19    Named(String),
20    /// A generic type with type arguments.
21    Generic {
22        /// Base type name (e.g., "HashMap", "Map", "Record").
23        base: String,
24        /// Type arguments (e.g., [String, Int] for HashMap<String, i64>).
25        args: Vec<TypeRef>,
26    },
27    /// A reference type (Rust: &T, others: usually no-op).
28    Ref(Box<TypeRef>),
29    /// A mutable reference type (Rust: &mut T).
30    RefMut(Box<TypeRef>),
31    /// Unit/void type.
32    Unit,
33    /// Result type with Ok and Err types.
34    Result {
35        /// The success type.
36        ok: Box<TypeRef>,
37        /// The error type.
38        err: Box<TypeRef>,
39    },
40}
41
42impl TypeRef {
43    /// Create a primitive type reference.
44    pub fn primitive(ty: PrimitiveType) -> Self {
45        Self::Primitive(ty)
46    }
47
48    /// Create an optional type reference.
49    pub fn optional(inner: TypeRef) -> Self {
50        Self::Optional(Box::new(inner))
51    }
52
53    /// Create an array type reference.
54    pub fn array(inner: TypeRef) -> Self {
55        Self::Array(Box::new(inner))
56    }
57
58    /// Create a named type reference.
59    pub fn named(name: impl Into<String>) -> Self {
60        Self::Named(name.into())
61    }
62
63    /// Create a generic type reference.
64    pub fn generic(base: impl Into<String>, args: Vec<TypeRef>) -> Self {
65        Self::Generic {
66            base: base.into(),
67            args,
68        }
69    }
70
71    /// Create a reference type.
72    pub fn ref_(inner: TypeRef) -> Self {
73        Self::Ref(Box::new(inner))
74    }
75
76    /// Create a mutable reference type.
77    pub fn ref_mut(inner: TypeRef) -> Self {
78        Self::RefMut(Box::new(inner))
79    }
80
81    /// Create a unit type.
82    pub fn unit() -> Self {
83        Self::Unit
84    }
85
86    /// Create a Result type.
87    pub fn result(ok: TypeRef, err: TypeRef) -> Self {
88        Self::Result {
89            ok: Box::new(ok),
90            err: Box::new(err),
91        }
92    }
93
94    /// Convenience: String type.
95    pub fn string() -> Self {
96        Self::Primitive(PrimitiveType::String)
97    }
98
99    /// Convenience: Int type.
100    pub fn int() -> Self {
101        Self::Primitive(PrimitiveType::Int)
102    }
103
104    /// Convenience: Float type.
105    pub fn float() -> Self {
106        Self::Primitive(PrimitiveType::Float)
107    }
108
109    /// Convenience: Bool type.
110    pub fn bool() -> Self {
111        Self::Primitive(PrimitiveType::Bool)
112    }
113
114    /// Convenience: Path type.
115    pub fn path() -> Self {
116        Self::Primitive(PrimitiveType::Path)
117    }
118
119    /// Convenience: Duration type.
120    pub fn duration() -> Self {
121        Self::Primitive(PrimitiveType::Duration)
122    }
123
124    /// Check if this type is optional.
125    pub fn is_optional(&self) -> bool {
126        matches!(self, Self::Optional(_))
127    }
128
129    /// Get the inner type for wrapper types (Optional, Array, Ref, RefMut).
130    ///
131    /// Returns `None` for non-wrapper types.
132    pub fn inner_type(&self) -> Option<&TypeRef> {
133        match self {
134            Self::Optional(inner) | Self::Array(inner) | Self::Ref(inner) | Self::RefMut(inner) => {
135                Some(inner)
136            }
137            _ => None,
138        }
139    }
140}
141
142/// Primitive types supported across languages.
143#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
144pub enum PrimitiveType {
145    /// String type (Rust: String, TS: string).
146    String,
147    /// Integer type (Rust: i64, TS: number, Go: int64).
148    Int,
149    /// Unsigned integer (Rust: u64, TS: number, Go: uint64).
150    UInt,
151    /// Float type (Rust: f64, TS: number, Go: float64).
152    Float,
153    /// Boolean type (Rust: bool, TS: boolean, Go: bool).
154    Bool,
155    /// Path type (Rust: PathBuf, TS: string, Go: string).
156    Path,
157    /// Duration type (Rust: Duration, TS: number, Go: time.Duration).
158    Duration,
159    /// Character type (Rust: char, TS: string).
160    Char,
161    /// Byte type (Rust: u8, TS: number, Go: byte).
162    Byte,
163}
164
165impl PrimitiveType {
166    /// Get the canonical name of this primitive.
167    pub fn as_str(&self) -> &'static str {
168        match self {
169            Self::String => "string",
170            Self::Int => "int",
171            Self::UInt => "uint",
172            Self::Float => "float",
173            Self::Bool => "bool",
174            Self::Path => "path",
175            Self::Duration => "duration",
176            Self::Char => "char",
177            Self::Byte => "byte",
178        }
179    }
180}
181
182/// Visibility/access level for types and members.
183#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
184pub enum Visibility {
185    /// Public (Rust: pub, TS: export, Go: capitalized).
186    #[default]
187    Public,
188    /// Private (Rust: no modifier, TS: no export, Go: lowercase).
189    Private,
190    /// Crate-level visibility (Rust: pub(crate)).
191    Crate,
192    /// Module-level visibility (Rust: pub(super)).
193    Super,
194}
195
196impl Visibility {
197    /// Check if this is a public visibility.
198    pub fn is_public(&self) -> bool {
199        matches!(self, Self::Public)
200    }
201
202    /// Check if this is a private visibility.
203    pub fn is_private(&self) -> bool {
204        matches!(self, Self::Private)
205    }
206}
207
208/// Trait for mapping types to language-specific representations.
209///
210/// Implement this trait to support a new target language's type system.
211pub trait TypeMapper {
212    /// Map a primitive type to the target language.
213    fn map_primitive(&self, ty: PrimitiveType) -> String;
214
215    /// Map an optional type (e.g., `Option<T>`, `T | undefined`).
216    fn map_optional(&self, inner: &str) -> String;
217
218    /// Map an array type (e.g., `Vec<T>`, `T[]`).
219    fn map_array(&self, inner: &str) -> String;
220
221    /// Map a reference type (Rust: `&T`, others usually no-op).
222    fn map_ref(&self, inner: &str) -> String {
223        inner.to_string()
224    }
225
226    /// Map a mutable reference type (Rust: `&mut T`).
227    fn map_ref_mut(&self, inner: &str) -> String {
228        inner.to_string()
229    }
230
231    /// Map a generic type with arguments.
232    fn map_generic(&self, base: &str, args: &[String]) -> String {
233        if args.is_empty() {
234            base.to_string()
235        } else {
236            format!("{}<{}>", base, args.join(", "))
237        }
238    }
239
240    /// Map a Result type (Rust: `Result<T, E>`, TS: `T`, Go: `(T, error)`).
241    fn map_result(&self, ok: &str, err: &str) -> String;
242
243    /// Map the unit type (Rust: `()`, TS: `void`, Go: empty).
244    fn map_unit(&self) -> String;
245
246    /// Render a complete TypeRef to a string.
247    fn render_type(&self, ty: &TypeRef) -> String {
248        match ty {
249            TypeRef::Primitive(p) => self.map_primitive(*p),
250            TypeRef::Optional(inner) => {
251                let inner_str = self.render_type(inner);
252                self.map_optional(&inner_str)
253            }
254            TypeRef::Array(inner) => {
255                let inner_str = self.render_type(inner);
256                self.map_array(&inner_str)
257            }
258            TypeRef::Named(name) => name.clone(),
259            TypeRef::Generic { base, args } => {
260                let arg_strs: Vec<_> = args.iter().map(|a| self.render_type(a)).collect();
261                self.map_generic(base, &arg_strs)
262            }
263            TypeRef::Ref(inner) => {
264                let inner_str = self.render_type(inner);
265                self.map_ref(&inner_str)
266            }
267            TypeRef::RefMut(inner) => {
268                let inner_str = self.render_type(inner);
269                self.map_ref_mut(&inner_str)
270            }
271            TypeRef::Unit => self.map_unit(),
272            TypeRef::Result { ok, err } => {
273                let ok_str = self.render_type(ok);
274                let err_str = self.render_type(err);
275                self.map_result(&ok_str, &err_str)
276            }
277        }
278    }
279}
280
281#[cfg(test)]
282mod tests {
283    use super::*;
284
285    #[test]
286    fn test_primitive_type_as_str() {
287        assert_eq!(PrimitiveType::String.as_str(), "string");
288        assert_eq!(PrimitiveType::Int.as_str(), "int");
289        assert_eq!(PrimitiveType::Bool.as_str(), "bool");
290    }
291
292    #[test]
293    fn test_type_ref_constructors() {
294        let string = TypeRef::string();
295        assert_eq!(string, TypeRef::Primitive(PrimitiveType::String));
296
297        let opt_string = TypeRef::optional(TypeRef::string());
298        assert!(matches!(opt_string, TypeRef::Optional(_)));
299
300        let arr_int = TypeRef::array(TypeRef::int());
301        assert!(matches!(arr_int, TypeRef::Array(_)));
302
303        let named = TypeRef::named("Context");
304        assert_eq!(named, TypeRef::Named("Context".into()));
305    }
306
307    #[test]
308    fn test_visibility() {
309        assert!(Visibility::Public.is_public());
310        assert!(!Visibility::Public.is_private());
311        assert!(Visibility::Private.is_private());
312        assert!(!Visibility::Private.is_public());
313    }
314
315    #[test]
316    fn test_generic_type() {
317        let map = TypeRef::generic("HashMap", vec![TypeRef::string(), TypeRef::int()]);
318        assert!(
319            matches!(map, TypeRef::Generic { base, args } if base == "HashMap" && args.len() == 2)
320        );
321    }
322}