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