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::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)$(¶ms[0])$(&impl_trait) };
171
172 for param in ¶ms[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}