1use genco::prelude::*;
2
3use self::{js_enum::js_enum, js_struct::js_struct};
4use crate::{
5 ast::{PrimitiveType, QualifiedIdentifier, Type, TypeBody, TypeDef},
6 codegen::{CodegenCx, ResolvedType},
7};
8
9pub use package::{js_module_gen, js_package_gen};
10
11pub(crate) mod common;
12pub(crate) mod encoder_common;
13mod js_enum;
14mod js_struct;
15mod package;
16
17pub fn js_type_def(cx: &CodegenCx, type_def: &TypeDef) -> js::Tokens {
18 let cx = &cx.with_type_params(&type_def.params);
19
20 match &type_def.body {
21 TypeBody::Struct(struct_def) => js_struct(cx, &type_def.name, &type_def.params, struct_def),
22 TypeBody::Enum(enum_def) => js_enum(cx, &type_def.name, &type_def.params, enum_def),
23 }
24}
25
26pub fn js_type_tokens(cx: &CodegenCx, ty: &Type) -> js::Tokens {
27 match ty {
28 Type::Primitive(PrimitiveType::Void) => quote! { void },
29 Type::Primitive(PrimitiveType::U8) => quote! { number },
30 Type::Primitive(PrimitiveType::U16) => quote! { number },
31 Type::Primitive(PrimitiveType::U32) => quote! { number },
32 Type::Primitive(PrimitiveType::U64) => quote! { bigint },
33 Type::Primitive(PrimitiveType::U128) => quote! { bigint },
34 Type::Primitive(PrimitiveType::I8) => quote! { number },
35 Type::Primitive(PrimitiveType::I16) => quote! { number },
36 Type::Primitive(PrimitiveType::I32) => quote! { number },
37 Type::Primitive(PrimitiveType::I64) => quote! { bigint },
38 Type::Primitive(PrimitiveType::I128) => quote! { bigint },
39 Type::Primitive(PrimitiveType::F32) => quote! { number },
40 Type::Primitive(PrimitiveType::F64) => quote! { number },
41 Type::Primitive(PrimitiveType::Bool) => quote! { boolean },
42 Type::Primitive(PrimitiveType::String) => quote! { string },
43 Type::Primitive(PrimitiveType::Box(inner_ty)) => js_type_tokens(cx, inner_ty),
44 Type::Primitive(PrimitiveType::List(item_ty)) => quote! {
45 $(js_type_tokens(cx, item_ty))[]
46 },
47 Type::Primitive(PrimitiveType::Option(item_ty)) => quote! {
48 $(js_type_tokens(cx, item_ty)) | null
49 },
50 Type::Primitive(PrimitiveType::Result(ok_ty, err_ty)) => quote! {
51 $(js::import("@modrpc-org/mproto", "Result"))<$(js_type_tokens(cx, ok_ty)), $(js_type_tokens(cx, err_ty))>
52 },
53 Type::Defined { ident, args } => match cx.resolve_type(ident) {
54 Some(ResolvedType::Defined(_)) => {
55 let args = js_type_args(cx, args, js_type_tokens);
56 let import = cx.js_import_qualified(ident);
57 quote! { $(import)$(args) }
58 }
59 Some(ResolvedType::UnboundParam) => {
60 quote! { $(&ident.name) }
61 }
62 Some(ResolvedType::BoundParam { value, .. }) => js_type_tokens(cx, value),
63 None => {
64 panic!("js_type_tokens failed to resolve type: {:?}", ident);
65 }
66 },
67 }
68}
69
70pub fn js_type_lazy_tokens(cx: &CodegenCx, ty: &Type) -> js::Tokens {
71 match ty {
72 Type::Primitive(PrimitiveType::Box(inner_ty)) => js_type_lazy_tokens(cx, &inner_ty),
73 Type::Primitive(PrimitiveType::List(item_ty)) => {
74 let list_lazy = js::import("@modrpc-org/mproto", "ListLazy");
75 quote! { $list_lazy<$(js_type_lazy_tokens(cx, &item_ty))> }
76 }
77 Type::Primitive(PrimitiveType::Option(inner_ty)) => {
78 let option = js::import("@modrpc-org/mproto", "Option");
79 quote! { $option<$(js_type_lazy_tokens(cx, &inner_ty))> }
80 }
81 Type::Primitive(PrimitiveType::Result(ok_ty, err_ty)) => quote! {
82 $(js::import("@modrpc-org/mproto", "Result"))<$(js_type_lazy_tokens(cx, ok_ty)), $(js_type_lazy_tokens(cx, err_ty))>
83 },
84 Type::Primitive(_) => js_type_tokens(cx, ty),
85 Type::Defined { ident, args } => {
86 match cx.resolve_type(ident) {
87 Some(ResolvedType::Defined(type_def)) => {
88 if type_def.body.is_enum() {
89 let args = js_type_args(cx, args, js_type_tokens);
91 let import = cx.js_import_qualified(&QualifiedIdentifier {
92 name: format!("{}", ident.name),
93 module: ident.module.clone(),
94 });
95 quote! { $(import)$(args) }
96 } else {
97 let args = js_type_args(cx, args, js_type_tokens);
98 let import = cx.js_import_qualified(&QualifiedIdentifier {
99 name: format!("{}Lazy", ident.name),
100 module: ident.module.clone(),
101 });
102 quote! { $(import)$(args) }
103 }
104 }
105 Some(ResolvedType::UnboundParam) => {
106 quote! { $(&ident.name) }
107 }
108 Some(ResolvedType::BoundParam { value, .. }) => js_type_lazy_tokens(cx, value),
109 None => {
110 panic!("js_type_lazy_tokens failed to resolve type: {:?}", ident);
111 }
112 }
113 }
114 }
115}
116
117pub fn js_type_args(
118 cx: &CodegenCx,
119 args: &[Type],
120 mut gen_tokens_fn: impl FnMut(&CodegenCx, &Type) -> js::Tokens,
121) -> js::Tokens {
122 if args.len() > 0 {
123 let arg_tokens = gen_tokens_fn(cx, &args[0]);
124 let mut args_items: js::Tokens = quote! { $arg_tokens };
125 for arg in &args[1..] {
126 let arg_tokens = gen_tokens_fn(cx, arg);
127 args_items = quote! { $args_items, $arg_tokens };
128 }
129
130 quote! { <$args_items> }
131 } else {
132 quote! {}
133 }
134}
135
136pub fn js_type_param_list(params: &[String]) -> js::Tokens {
137 if params.len() == 0 {
138 Tokens::new()
139 } else {
140 let mut tokens = quote! { <$(¶ms[0]) };
141
142 for param in ¶ms[1..] {
143 tokens = quote! { $tokens, $param };
144 }
145
146 tokens = quote! { $tokens> };
147
148 tokens
149 }
150}
151
152pub fn js_encoder_type_args(
153 cx: &CodegenCx,
154 args: &[Type],
155 mut gen_tokens_fn: impl FnMut(&CodegenCx, &Type) -> js::Tokens,
156) -> js::Tokens {
157 if args.len() > 0 {
158 let arg_tokens = gen_tokens_fn(cx, &args[0]);
159 let mut args_items: js::Tokens = quote! { $arg_tokens };
160 for arg in &args[1..] {
161 let arg_tokens = gen_tokens_fn(cx, arg);
162 args_items = quote! { $args_items, $arg_tokens };
163 }
164
165 quote! { $args_items }
166 } else {
167 quote! {}
168 }
169}
170
171pub fn js_encoder_type_args_enclosed(
172 cx: &CodegenCx,
173 args: &[Type],
174 gen_tokens_fn: impl FnMut(&CodegenCx, &Type) -> js::Tokens,
175) -> js::Tokens {
176 if args.len() > 0 {
177 quote! { ($(js_encoder_type_args(cx, args, gen_tokens_fn))) }
178 } else {
179 quote! {}
180 }
181}
182
183pub fn js_type_encoder(cx: &CodegenCx, ty: &Type) -> js::Tokens {
184 match ty {
185 Type::Primitive(PrimitiveType::Void) => {
186 quote! { $(js::import("@modrpc-org/mproto", "ProtoVoid")) }
187 }
188 Type::Primitive(PrimitiveType::U8) => {
189 quote! { $(js::import("@modrpc-org/mproto", "ProtoUint8")) }
190 }
191 Type::Primitive(PrimitiveType::U16) => {
192 quote! { $(js::import("@modrpc-org/mproto", "ProtoUint16")) }
193 }
194 Type::Primitive(PrimitiveType::U32) => {
195 quote! { $(js::import("@modrpc-org/mproto", "ProtoUint32")) }
196 }
197 Type::Primitive(PrimitiveType::U64) => {
198 quote! { $(js::import("@modrpc-org/mproto", "ProtoUint64")) }
199 }
200 Type::Primitive(PrimitiveType::U128) => {
201 quote! { $(js::import("@modrpc-org/mproto", "ProtoUint128")) }
202 }
203 Type::Primitive(PrimitiveType::I8) => {
204 quote! { $(js::import("@modrpc-org/mproto", "ProtoInt8")) }
205 }
206 Type::Primitive(PrimitiveType::I16) => {
207 quote! { $(js::import("@modrpc-org/mproto", "ProtoInt16")) }
208 }
209 Type::Primitive(PrimitiveType::I32) => {
210 quote! { $(js::import("@modrpc-org/mproto", "ProtoInt32")) }
211 }
212 Type::Primitive(PrimitiveType::I64) => {
213 quote! { $(js::import("@modrpc-org/mproto", "ProtoInt64")) }
214 }
215 Type::Primitive(PrimitiveType::I128) => {
216 quote! { $(js::import("@modrpc-org/mproto", "ProtoInt128")) }
217 }
218 Type::Primitive(PrimitiveType::String) => {
219 quote! { $(js::import("@modrpc-org/mproto", "ProtoString")) }
220 }
221 Type::Primitive(PrimitiveType::F32) => {
222 quote! { $(js::import("@modrpc-org/mproto", "ProtoFloat32")) }
223 }
224 Type::Primitive(PrimitiveType::F64) => {
225 quote! { $(js::import("@modrpc-org/mproto", "ProtoFloat64")) }
226 }
227 Type::Primitive(PrimitiveType::Bool) => {
228 quote! { $(js::import("@modrpc-org/mproto", "ProtoBool")) }
229 }
230 Type::Primitive(PrimitiveType::Box(inner_ty)) => quote! {
231 $(js::import("@modrpc-org/mproto", "ProtoBox"))($(js_type_encoder(cx, inner_ty)))
232 },
233 Type::Primitive(PrimitiveType::List(item_ty)) => quote! {
234 $(js::import("@modrpc-org/mproto", "ProtoList"))($(js_type_encoder(cx, item_ty)))
235 },
236 Type::Primitive(PrimitiveType::Option(inner_ty)) => quote! {
237 $(js::import("@modrpc-org/mproto", "ProtoOption"))($(js_type_encoder(cx, inner_ty)))
238 },
239 Type::Primitive(PrimitiveType::Result(ok_ty, err_ty)) => quote! {
240 $(js::import("@modrpc-org/mproto", "ProtoResult"))($(js_type_encoder(cx, ok_ty)), $(js_type_encoder(cx, err_ty)))
241 },
242 Type::Defined { ident, args } => match cx.resolve_type(ident) {
243 Some(ResolvedType::Defined(_)) => {
244 let args = js_encoder_type_args_enclosed(cx, args, js_type_encoder);
245 let import = cx.js_import_qualified(&QualifiedIdentifier {
246 name: format!("Proto{}", ident.name),
247 module: ident.module.clone(),
248 });
249 quote! { $(import)$(args) }
250 }
251 Some(ResolvedType::UnboundParam) => {
252 quote! { this.$(&ident.name)Encoder }
253 }
254 Some(ResolvedType::BoundParam { value, .. }) => js_type_encoder(cx, value),
255 None => {
256 panic!("js_type_encoder failed to resolve type: {:?}", ident);
257 }
258 },
259 }
260}
261
262pub fn js_type_lazy_encoder(cx: &CodegenCx, ty: &Type) -> js::Tokens {
263 match ty {
264 Type::Primitive(PrimitiveType::Box(item_ty)) => quote! {
265 $(js::import("@modrpc-org/mproto", "ProtoBoxLazy"))($(js_type_lazy_encoder(cx, item_ty)))
266 },
267 Type::Primitive(PrimitiveType::List(item_ty)) => quote! {
268 $(js::import("@modrpc-org/mproto", "ProtoListLazy"))($(js_type_lazy_encoder(cx, item_ty)))
269 },
270 Type::Primitive(PrimitiveType::Option(inner_ty)) => quote! {
271 $(js::import("@modrpc-org/mproto", "ProtoOptionLazy"))($(js_type_lazy_encoder(cx, inner_ty)))
272 },
273 Type::Primitive(PrimitiveType::Result(ok_ty, err_ty)) => quote! {
274 $(js::import("@modrpc-org/mproto", "ProtoResultLazy"))($(js_type_lazy_encoder(cx, ok_ty)), $(js_type_lazy_encoder(cx, err_ty)))
275 },
276 Type::Primitive(_) => js_type_encoder(cx, ty),
277 Type::Defined { ident, args } => {
278 match cx.resolve_type(ident) {
279 Some(ResolvedType::Defined(type_def)) => {
280 if type_def.body.is_enum() {
281 let args = js_encoder_type_args_enclosed(cx, args, js_type_encoder);
283 let import = cx.js_import_qualified(&QualifiedIdentifier {
284 name: format!("Proto{}", ident.name),
285 module: ident.module.clone(),
286 });
287 quote! { $(import)$(args) }
288 } else {
289 let args = js_encoder_type_args_enclosed(cx, args, js_type_encoder);
290 let import = cx.js_import_qualified(&QualifiedIdentifier {
291 name: format!("Proto{}Lazy", ident.name),
292 module: ident.module.clone(),
293 });
294 quote! { $(import)$(args) }
295 }
296 }
297 Some(ResolvedType::UnboundParam) => {
298 quote! { this.$(&ident.name)Encoder }
299 }
300 Some(ResolvedType::BoundParam { value, .. }) => js_type_lazy_encoder(cx, value),
301 None => {
302 panic!("js_type_lazy_encoder failed to resolve type: {:?}", ident);
303 }
304 }
305 }
306 }
307}