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