mproto_codegen/codegen/rust/
mod.rs

1use genco::prelude::*;
2
3use self::{common::lazy_type_requires_lifetime, rust_enum::rust_enum, rust_struct::rust_struct};
4use crate::{
5    ast,
6    codegen::{CodegenCx, ResolvedType},
7};
8
9pub use package::{rust_module_gen, rust_package_gen};
10
11mod common;
12mod package;
13mod rust_enum;
14mod rust_struct;
15
16pub fn rust_type_def(cx: &CodegenCx, type_def: &ast::TypeDef) -> rust::Tokens {
17    let cx = &cx.with_type_params(&type_def.params);
18
19    match &type_def.body {
20        ast::TypeBody::Struct(struct_def) => {
21            rust_struct(cx, &type_def.name, &type_def.params, struct_def)
22        }
23        ast::TypeBody::Enum(enum_def) => rust_enum(cx, &type_def.name, &type_def.params, enum_def),
24    }
25}
26
27pub fn rust_type_tokens(cx: &CodegenCx, ty: &ast::Type) -> rust::Tokens {
28    match ty {
29        ast::Type::Primitive(ast::PrimitiveType::Void) => quote! { () },
30        ast::Type::Primitive(ast::PrimitiveType::U8) => quote! { u8 },
31        ast::Type::Primitive(ast::PrimitiveType::U16) => quote! { u16 },
32        ast::Type::Primitive(ast::PrimitiveType::U32) => quote! { u32 },
33        ast::Type::Primitive(ast::PrimitiveType::U64) => quote! { u64 },
34        ast::Type::Primitive(ast::PrimitiveType::U128) => quote! { u128 },
35        ast::Type::Primitive(ast::PrimitiveType::I8) => quote! { i8 },
36        ast::Type::Primitive(ast::PrimitiveType::I16) => quote! { i16 },
37        ast::Type::Primitive(ast::PrimitiveType::I32) => quote! { i32 },
38        ast::Type::Primitive(ast::PrimitiveType::I64) => quote! { i64 },
39        ast::Type::Primitive(ast::PrimitiveType::I128) => quote! { i128 },
40        ast::Type::Primitive(ast::PrimitiveType::F32) => quote! { f32 },
41        ast::Type::Primitive(ast::PrimitiveType::F64) => quote! { f64 },
42        ast::Type::Primitive(ast::PrimitiveType::Bool) => quote! { bool },
43        ast::Type::Primitive(ast::PrimitiveType::String) => quote! { String },
44        ast::Type::Primitive(ast::PrimitiveType::Box(inner_ty)) => quote! {
45            Box<$(rust_type_tokens(cx, inner_ty))>
46        },
47        ast::Type::Primitive(ast::PrimitiveType::List(item_ty)) => quote! {
48            Vec<$(rust_type_tokens(cx, item_ty))>
49        },
50        ast::Type::Primitive(ast::PrimitiveType::Option(item_ty)) => quote! {
51            Option<$(rust_type_tokens(cx, item_ty))>
52        },
53        ast::Type::Primitive(ast::PrimitiveType::Result(ok_ty, err_ty)) => quote! {
54            Result<$(rust_type_tokens(cx, ok_ty)), $(rust_type_tokens(cx, err_ty))>
55        },
56        ast::Type::Defined { ident, args } => match cx.resolve_type(ident) {
57            Some(ResolvedType::Defined(_)) => {
58                let args_tokens = rust_type_arg_list(cx, args, None);
59                quote! { $(cx.rust_import_qualified(ident))$args_tokens }
60            }
61            Some(ResolvedType::UnboundParam) => {
62                quote! { $(&ident.name) }
63            }
64            Some(ResolvedType::BoundParam { value, .. }) => rust_type_tokens(cx, value),
65            None => {
66                panic!("rust_type_tokens failed to resolve type: {:?}", ident);
67            }
68        },
69    }
70}
71
72pub fn rust_type_lazy_tokens(cx: &CodegenCx, ty: &ast::Type) -> rust::Tokens {
73    match ty {
74        ast::Type::Primitive(ast::PrimitiveType::Void) => quote! { () },
75        ast::Type::Primitive(ast::PrimitiveType::U8) => quote! { u8 },
76        ast::Type::Primitive(ast::PrimitiveType::U16) => quote! { u16 },
77        ast::Type::Primitive(ast::PrimitiveType::U32) => quote! { u32 },
78        ast::Type::Primitive(ast::PrimitiveType::U64) => quote! { u64 },
79        ast::Type::Primitive(ast::PrimitiveType::U128) => quote! { u128 },
80        ast::Type::Primitive(ast::PrimitiveType::I8) => quote! { i8 },
81        ast::Type::Primitive(ast::PrimitiveType::I16) => quote! { i16 },
82        ast::Type::Primitive(ast::PrimitiveType::I32) => quote! { i32 },
83        ast::Type::Primitive(ast::PrimitiveType::I64) => quote! { i64 },
84        ast::Type::Primitive(ast::PrimitiveType::I128) => quote! { i128 },
85        ast::Type::Primitive(ast::PrimitiveType::F32) => quote! { f32 },
86        ast::Type::Primitive(ast::PrimitiveType::F64) => quote! { f64 },
87        ast::Type::Primitive(ast::PrimitiveType::Bool) => quote! { bool },
88        ast::Type::Primitive(ast::PrimitiveType::String) => quote! { &'a str },
89        ast::Type::Primitive(ast::PrimitiveType::Box(inner_ty)) => quote! {
90            $(rust_type_tokens(cx, inner_ty))
91        },
92        ast::Type::Primitive(ast::PrimitiveType::List(item_ty)) => quote! {
93            $(rust::import("mproto", "ListLazy").qualified())<'a, $(rust_type_tokens(cx, item_ty))>
94        },
95        ast::Type::Primitive(ast::PrimitiveType::Option(item_ty)) => quote! {
96            Option<$(rust_type_lazy_tokens(cx, item_ty))>
97        },
98        ast::Type::Primitive(ast::PrimitiveType::Result(ok_ty, err_ty)) => quote! {
99            Result<$(rust_type_lazy_tokens(cx, ok_ty)), $(rust_type_lazy_tokens(cx, err_ty))>
100        },
101        ast::Type::Defined { ident, args } => match cx.resolve_type(ident) {
102            Some(ResolvedType::Defined(_)) => {
103                let maybe_lifetime = if lazy_type_requires_lifetime(cx.db, ty) {
104                    Some(quote! { 'a })
105                } else {
106                    None
107                };
108                let args_tokens = rust_type_arg_list(cx, args, maybe_lifetime);
109                let ref_ident = ast::QualifiedIdentifier {
110                    name: format!("{}Lazy", ident.name),
111                    module: ident.module.clone(),
112                };
113                quote! { $(cx.rust_import_qualified(&ref_ident))$(args_tokens) }
114            }
115            Some(ResolvedType::UnboundParam) => {
116                quote! { $(&ident.name)::Lazy<'a> }
117            }
118            Some(ResolvedType::BoundParam { value, .. }) => rust_type_lazy_tokens(cx, value),
119            None => {
120                panic!("rust_type_lazy_tokens failed to resolve type: {:?}", ident);
121            }
122        },
123    }
124}
125
126pub fn rust_type_arg_list(
127    cx: &CodegenCx,
128    args: &[ast::Type],
129    lifetimes: Option<rust::Tokens>,
130) -> rust::Tokens {
131    if args.len() == 0 {
132        if let Some(lifetimes) = lifetimes {
133            quote! { <$lifetimes> }
134        } else {
135            quote! {}
136        }
137    } else {
138        let lifetimes = lifetimes
139            .map(|l| quote! { $l,$(" ") })
140            .unwrap_or(rust::Tokens::new());
141
142        let mut args_items: rust::Tokens = quote! {
143            $(lifetimes)$(rust_type_tokens(cx, &args[0]))
144        };
145        for arg in &args[1..] {
146            args_items = quote! { $args_items, $(rust_type_tokens(cx, arg)) };
147        }
148
149        quote! { <$args_items> }
150    }
151}
152
153pub fn rust_type_param_list(
154    params: &[String],
155    lifetimes: Option<rust::Tokens>,
156    impl_trait: Option<rust::Tokens>,
157) -> rust::Tokens {
158    if params.len() == 0 {
159        if let Some(lifetimes) = lifetimes {
160            quote! { <$lifetimes> }
161        } else {
162            Tokens::new()
163        }
164    } else {
165        let lifetimes = lifetimes
166            .map(|l| quote! { $l,$(" ") })
167            .unwrap_or(Tokens::new());
168        let impl_trait = impl_trait.map(|i| quote! { : $i }).unwrap_or(Tokens::new());
169
170        let mut tokens = quote! { <$(lifetimes)$(&params[0])$(&impl_trait) };
171
172        for param in &params[1..] {
173            tokens = quote! { $tokens, $(param)$(&impl_trait) };
174        }
175
176        tokens = quote! { $tokens> };
177
178        tokens
179    }
180}
181
182pub fn rust_type_default_value(cx: &CodegenCx, ty: &ast::Type) -> rust::Tokens {
183    match &ty {
184        ast::Type::Primitive(ast::PrimitiveType::Void) => quote! { () },
185        ast::Type::Primitive(ast::PrimitiveType::U8) => quote! { 0 },
186        ast::Type::Primitive(ast::PrimitiveType::U16) => quote! { 0 },
187        ast::Type::Primitive(ast::PrimitiveType::U32) => quote! { 0 },
188        ast::Type::Primitive(ast::PrimitiveType::U64) => quote! { 0 },
189        ast::Type::Primitive(ast::PrimitiveType::U128) => quote! { 0 },
190        ast::Type::Primitive(ast::PrimitiveType::I8) => quote! { 0 },
191        ast::Type::Primitive(ast::PrimitiveType::I16) => quote! { 0 },
192        ast::Type::Primitive(ast::PrimitiveType::I32) => quote! { 0 },
193        ast::Type::Primitive(ast::PrimitiveType::I64) => quote! { 0 },
194        ast::Type::Primitive(ast::PrimitiveType::I128) => quote! { 0 },
195        ast::Type::Primitive(ast::PrimitiveType::F32) => quote! { 0.0 },
196        ast::Type::Primitive(ast::PrimitiveType::F64) => quote! { 0.0 },
197        ast::Type::Primitive(ast::PrimitiveType::Bool) => quote! { false },
198        ast::Type::Primitive(ast::PrimitiveType::String) => quote! { 0 },
199        ast::Type::Primitive(ast::PrimitiveType::Box(inner_ty)) => quote! {
200            Box::new($(rust_type_default_value(cx, inner_ty)))
201        },
202        ast::Type::Primitive(ast::PrimitiveType::List(_)) => quote! { [] },
203        ast::Type::Primitive(ast::PrimitiveType::Option(_)) => quote! { None },
204        ast::Type::Primitive(ast::PrimitiveType::Result(ok_ty, _)) => quote! {
205            Ok($(rust_type_default_value(cx, ok_ty)))
206        },
207        ast::Type::Defined { ident, .. } => {
208            if let Some(type_def) = cx.db.lookup_type_def(ident) {
209                match &type_def.body {
210                    ast::TypeBody::Struct(s) => rust_struct_default_value(cx, &ident, s),
211                    ast::TypeBody::Enum(e) => rust_enum_default_value(cx, &ident, e),
212                }
213            } else {
214                quote! { todo!() }
215            }
216        }
217    }
218}
219
220pub fn rust_struct_default_value(
221    cx: &CodegenCx,
222    ident: &ast::QualifiedIdentifier,
223    s: &ast::Struct,
224) -> rust::Tokens {
225    let mut params = rust::Tokens::new();
226    for field in &s.fields {
227        quote_in! { params =>
228            $(&field.name): $(rust_type_default_value(cx, &field.ty)),
229        };
230    }
231
232    quote! {
233        $(cx.rust_import_qualified(ident)) {
234            $params
235        }
236    }
237}
238
239pub fn rust_enum_default_value(
240    cx: &CodegenCx,
241    ident: &ast::QualifiedIdentifier,
242    e: &ast::Enum,
243) -> rust::Tokens {
244    let (variant_name, variant) = &e.variants[0];
245    let enum_import = cx.rust_import_qualified(ident);
246
247    match variant {
248        ast::EnumVariant::Empty => {
249            quote! {
250                $(enum_import)::$(variant_name)
251            }
252        }
253        ast::EnumVariant::NamedFields { fields } => {
254            let mut params = rust::Tokens::new();
255            for field in fields {
256                quote_in! { params =>
257                    $(&field.name): $(rust_type_default_value(cx, &field.ty)),
258                };
259            }
260
261            quote! {
262                $(enum_import)::$(variant_name) {
263                    $params
264                }
265            }
266        }
267    }
268}