1use std::collections::HashMap;
2use std::fmt::Write;
3use trans_gen_core::trans_schema::*;
4use trans_gen_core::Writer;
5
6fn conv(name: &str) -> String {
7 name.replace("Bool", "Boolean")
8 .replace("Int32", "Int")
9 .replace("Int64", "Long")
10 .replace("Float32", "Float")
11 .replace("Float64", "Double")
12}
13
14pub struct Generator {
15 files: HashMap<String, String>,
16}
17
18fn type_name(schema: &Schema) -> String {
19 match schema {
20 Schema::Bool => "Boolean".to_owned(),
21 Schema::Int32 => "Int".to_owned(),
22 Schema::Int64 => "Long".to_owned(),
23 Schema::Float32 => "Float".to_owned(),
24 Schema::Float64 => "Double".to_owned(),
25 Schema::String => "String".to_owned(),
26 Schema::Struct(Struct { name, .. })
27 | Schema::OneOf {
28 base_name: name, ..
29 }
30 | Schema::Enum {
31 base_name: name, ..
32 } => format!("model.{}", name.camel_case(conv)),
33 Schema::Option(inner) => format!("{}?", type_name(inner)),
34 Schema::Vec(inner) => format!("Array<{}>", type_name(inner)),
35 Schema::Map(key, value) => format!("MutableMap<{}, {}>", type_name(key), type_name(value)),
36 }
37}
38
39fn default_value(schema: &Schema) -> String {
40 match schema {
41 Schema::Bool => "false".to_owned(),
42 Schema::Int32 => "0".to_owned(),
43 Schema::Int64 => "0L".to_owned(),
44 Schema::Float32 => "0.0f".to_owned(),
45 Schema::Float64 => "0.0".to_owned(),
46 Schema::Option(_) => "null".to_owned(),
47 Schema::String
48 | Schema::Struct(_)
49 | Schema::OneOf { .. }
50 | Schema::Enum { .. }
51 | Schema::Vec(_)
52 | Schema::Map(_, _) => unreachable!(),
53 }
54}
55
56fn can_lateinit(schema: &Schema) -> bool {
57 match schema {
58 Schema::Bool
59 | Schema::Int32
60 | Schema::Int64
61 | Schema::Float32
62 | Schema::Float64
63 | Schema::Option(_) => false,
64 Schema::String
65 | Schema::Struct(_)
66 | Schema::OneOf { .. }
67 | Schema::Enum { .. }
68 | Schema::Vec(_)
69 | Schema::Map(_, _) => true,
70 }
71}
72
73fn index_var_name(index_var: &mut usize) -> String {
74 let result = "ijk".chars().nth(*index_var).unwrap();
75 *index_var += 1;
76 result.to_string()
77}
78
79fn var_name(name: &str) -> &str {
80 match name.rfind('.') {
81 Some(index) => &name[(index + 1)..],
82 None => name,
83 }
84}
85
86fn getter_prefix(schema: &Schema) -> &'static str {
87 match schema {
88 Schema::Bool => "is",
89 _ => "get",
90 }
91}
92
93fn write_struct(
94 writer: &mut Writer,
95 struc: &Struct,
96 base: Option<(&Name, usize)>,
97) -> std::fmt::Result {
98 if let Some((base_name, _)) = base {
100 writeln!(
101 writer,
102 "class {} : {} {{",
103 struc.name.camel_case(conv),
104 base_name.camel_case(conv)
105 )?;
106 } else {
107 writeln!(writer, "class {} {{", struc.name.camel_case(conv))?;
108 }
109 writer.inc_ident();
110
111 for field in &struc.fields {
113 writeln!(
114 writer,
115 "{}var {}: {}{}",
116 if can_lateinit(&field.schema) {
117 "lateinit "
118 } else {
119 ""
120 },
121 field.name.mixed_case(conv),
122 type_name(&field.schema),
123 if can_lateinit(&field.schema) {
124 String::new()
125 } else {
126 format!(" = {}", default_value(&field.schema))
127 }
128 )?;
129 }
130
131 writeln!(writer, "constructor() {{}}")?;
133 if !struc.fields.is_empty() {
134 write!(writer, "constructor(")?;
135 for (index, field) in struc.fields.iter().enumerate() {
136 if index > 0 {
137 write!(writer, ", ")?;
138 }
139 write!(
140 writer,
141 "{}: {}",
142 field.name.mixed_case(conv),
143 type_name(&field.schema),
144 )?;
145 }
146 writeln!(writer, ") {{")?;
147 for field in &struc.fields {
148 writeln!(
149 writer,
150 " this.{} = {}",
151 field.name.mixed_case(conv),
152 field.name.mixed_case(conv)
153 )?;
154 }
155 writeln!(writer, "}}")?;
156 }
157
158 writeln!(writer, "companion object {{")?;
160 writer.inc_ident();
161 if let Some((_, discriminant)) = base {
162 writeln!(writer, "val TAG = {}", discriminant)?;
163 }
164 writeln!(writer, "@Throws(java.io.IOException::class)")?;
165 writeln!(
166 writer,
167 "fun readFrom(stream: java.io.InputStream): {} {{",
168 struc.name.camel_case(conv),
169 )?;
170 writer.inc_ident();
171 writeln!(writer, "val result = {}()", struc.name.camel_case(conv),)?;
172 for field in &struc.fields {
173 fn assign(
174 writer: &mut Writer,
175 to: &str,
176 schema: &Schema,
177 index_var: &mut usize,
178 ) -> std::fmt::Result {
179 match schema {
180 Schema::Bool => {
181 writeln!(writer, "{} = StreamUtil.readBoolean(stream)", to)?;
182 }
183 Schema::Int32 => {
184 writeln!(writer, "{} = StreamUtil.readInt(stream)", to)?;
185 }
186 Schema::Int64 => {
187 writeln!(writer, "{} = StreamUtil.readLong(stream)", to)?;
188 }
189 Schema::Float32 => {
190 writeln!(writer, "{} = StreamUtil.readFloat(stream)", to)?;
191 }
192 Schema::Float64 => {
193 writeln!(writer, "{} = StreamUtil.readDouble(stream)", to)?;
194 }
195 Schema::String => {
196 writeln!(writer, "{} = StreamUtil.readString(stream)", to)?;
197 }
198 Schema::Struct(Struct { name, .. })
199 | Schema::OneOf {
200 base_name: name, ..
201 } => {
202 writeln!(
203 writer,
204 "{} = model.{}.readFrom(stream)",
205 to,
206 name.camel_case(conv)
207 )?;
208 }
209 Schema::Option(inner) => {
210 writeln!(writer, "if (StreamUtil.readBoolean(stream)) {{")?;
211 writer.inc_ident();
212 assign(writer, to, inner, index_var)?;
213 writer.dec_ident();
214 writeln!(writer, "}} else {{")?;
215 writeln!(writer, " {} = null", to)?;
216 writeln!(writer, "}}")?;
217 }
218 Schema::Vec(inner) => {
219 writeln!(writer, "{} = Array(StreamUtil.readInt(stream), {{", to)?;
220 writer.inc_ident();
221 writeln!(writer, "var {}Value: {}", var_name(to), type_name(inner))?;
222 assign(writer, &format!("{}Value", var_name(to)), inner, index_var)?;
223 writeln!(writer, "{}Value", var_name(to))?;
224 writer.dec_ident();
225 writeln!(writer, "}})")?;
226 }
227 Schema::Map(key_type, value_type) => {
228 let to_size = format!("{}Size", var_name(to));
229 writeln!(writer, "val {} = StreamUtil.readInt(stream)", to_size)?;
230 writeln!(writer, "{} = mutableMapOf()", to)?;
231 let index_var_name = index_var_name(index_var);
232 writeln!(writer, "for ({} in 0 until {}) {{", index_var_name, to_size)?;
233 writer.inc_ident();
234 writeln!(writer, "var {}Key: {}", var_name(to), type_name(key_type))?;
235 assign(writer, &format!("{}Key", var_name(to)), key_type, index_var)?;
236 writeln!(
237 writer,
238 "var {}Value: {}",
239 var_name(to),
240 type_name(value_type)
241 )?;
242 assign(
243 writer,
244 &format!("{}Value", var_name(to)),
245 value_type,
246 index_var,
247 )?;
248 writeln!(
249 writer,
250 "{}.put({}Key, {}Value)",
251 to,
252 var_name(to),
253 var_name(to)
254 )?;
255 writer.dec_ident();
256 writeln!(writer, "}}")?;
257 }
258 Schema::Enum {
259 base_name,
260 variants,
261 } => {
262 writeln!(writer, "when (StreamUtil.readInt(stream)) {{")?;
263 for (discriminant, variant) in variants.iter().enumerate() {
264 write!(writer, "{} ->", discriminant)?;
265 writeln!(
266 writer,
267 "{} = model.{}.{}",
268 to,
269 base_name.camel_case(conv),
270 variant.shouty_snake_case(conv)
271 )?;
272 }
273 writeln!(
274 writer,
275 "else -> throw java.io.IOException(\"Unexpected discriminant value\")"
276 )?;
277 writeln!(writer, "}}")?;
278 }
279 }
280 Ok(())
281 }
282 assign(
283 writer,
284 &format!("result.{}", field.name.mixed_case(conv)),
285 &field.schema,
286 &mut 0,
287 )?;
288 }
289 writeln!(writer, "return result")?;
290 writer.dec_ident();
291 writeln!(writer, "}}")?;
292 writer.dec_ident();
293 writeln!(writer, "}}")?;
294
295 writeln!(writer, "@Throws(java.io.IOException::class)")?;
297 writeln!(
298 writer,
299 "{}fun writeTo(stream: java.io.OutputStream) {{",
300 if base.is_some() { "override " } else { "" }
301 )?;
302 writer.inc_ident();
303 if base.is_some() {
304 writeln!(writer, "StreamUtil.writeInt(stream, TAG)")?;
305 }
306 if let Some(magic) = struc.magic {
307 writeln!(writer, "StreamUtil.writeInt(stream, {})", magic)?;
308 }
309 for field in &struc.fields {
310 fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
311 match schema {
312 Schema::Bool => {
313 writeln!(writer, "StreamUtil.writeBoolean(stream, {})", value)?;
314 }
315 Schema::Int32 => {
316 writeln!(writer, "StreamUtil.writeInt(stream, {})", value)?;
317 }
318 Schema::Int64 => {
319 writeln!(writer, "StreamUtil.writeLong(stream, {})", value)?;
320 }
321 Schema::Float32 => {
322 writeln!(writer, "StreamUtil.writeFloat(stream, {})", value)?;
323 }
324 Schema::Float64 => {
325 writeln!(writer, "StreamUtil.writeDouble(stream, {})", value)?;
326 }
327 Schema::String => {
328 writeln!(writer, "StreamUtil.writeString(stream, {})", value)?;
329 }
330 Schema::Struct(_) | Schema::OneOf { .. } => {
331 writeln!(writer, "{}.writeTo(stream)", value)?;
332 }
333 Schema::Option(inner) => {
334 writeln!(writer, "val {} = {};", var_name(value), value)?;
335 writeln!(writer, "if ({} == null) {{", var_name(value))?;
336 writeln!(writer, " StreamUtil.writeBoolean(stream, false)")?;
337 writeln!(writer, "}} else {{")?;
338 writer.inc_ident();
339 writeln!(writer, "StreamUtil.writeBoolean(stream, true)")?;
340 write(writer, &var_name(value), inner)?;
341 writer.dec_ident();
342 writeln!(writer, "}}")?;
343 }
344 Schema::Vec(inner) => {
345 writeln!(writer, "StreamUtil.writeInt(stream, {}.size)", value)?;
346 writeln!(writer, "for ({}Element in {}) {{", var_name(value), value)?;
347 writer.inc_ident();
348 write(writer, &format!("{}Element", var_name(value)), inner)?;
349 writer.dec_ident();
350 writeln!(writer, "}}")?;
351 }
352 Schema::Map(key_type, value_type) => {
353 writeln!(writer, "StreamUtil.writeInt(stream, {}.size)", value)?;
354 writeln!(writer, "for ({}Entry in {}) {{", var_name(value), value,)?;
355 writer.inc_ident();
356 write(writer, &format!("{}Entry.key", var_name(value)), key_type)?;
357 write(
358 writer,
359 &format!("{}Entry.value", var_name(value)),
360 value_type,
361 )?;
362 writer.dec_ident();
363 writeln!(writer, "}}")?;
364 }
365 Schema::Enum { .. } => {
366 writeln!(
367 writer,
368 "StreamUtil.writeInt(stream, {}.discriminant)",
369 value
370 )?;
371 }
372 }
373 Ok(())
374 }
375 write(writer, &field.name.mixed_case(conv), &field.schema)?;
376 }
377 writer.dec_ident();
378 writeln!(writer, "}}")?;
379 writer.dec_ident();
380 writeln!(writer, "}}")?;
381 Ok(())
382}
383
384impl trans_gen_core::Generator for Generator {
385 fn new(name: &str, version: &str) -> Self {
386 let mut files = HashMap::new();
387 files.insert(
388 "util/StreamUtil.kt".to_owned(),
389 include_str!("../template/StreamUtil.kt").to_owned(),
390 );
391 Self { files }
392 }
393 fn result(self) -> HashMap<String, String> {
394 self.files
395 }
396 fn add_only(&mut self, schema: &Schema) {
397 match schema {
398 Schema::Enum {
399 base_name,
400 variants,
401 } => {
402 let file_name = format!("model/{}.kt", base_name.camel_case(conv));
403 let mut writer = Writer::new();
404 writeln!(writer, "package model").unwrap();
405 writeln!(writer).unwrap();
406 writeln!(writer, "import util.StreamUtil").unwrap();
407 writeln!(writer).unwrap();
408 writeln!(
409 writer,
410 "enum class {} private constructor(var discriminant: Int) {{",
411 base_name.camel_case(conv)
412 )
413 .unwrap();
414 writer.inc_ident();
415 for (index, variant) in variants.iter().enumerate() {
416 writeln!(
417 writer,
418 "{}({}){}",
419 variant.shouty_snake_case(conv),
420 index,
421 if index + 1 < variants.len() { "," } else { "" }
422 )
423 .unwrap();
424 }
425 writer.dec_ident();
426 writeln!(writer, "}}").unwrap();
427 self.files.insert(file_name, writer.get());
428 }
429 Schema::Struct(struc) => {
430 let file_name = format!("model/{}.kt", struc.name.camel_case(conv));
431 let mut writer = Writer::new();
432 writeln!(writer, "package model").unwrap();
433 writeln!(writer).unwrap();
434 writeln!(writer, "import util.StreamUtil").unwrap();
435 writeln!(writer).unwrap();
436 write_struct(&mut writer, struc, None).unwrap();
437 self.files.insert(file_name, writer.get());
438 }
439 Schema::OneOf {
440 base_name,
441 variants,
442 } => {
443 let file_name = format!("model/{}.kt", base_name.camel_case(conv));
444 let mut writer = Writer::new();
445 writeln!(writer, "package model").unwrap();
446 writeln!(writer).unwrap();
447 writeln!(writer, "import util.StreamUtil").unwrap();
448 writeln!(writer).unwrap();
449 writeln!(writer, "abstract class {} {{", base_name.camel_case(conv)).unwrap();
450 {
451 writer.inc_ident();
452 writeln!(writer, "@Throws(java.io.IOException::class)").unwrap();
453 writeln!(writer, "abstract fun writeTo(stream: java.io.OutputStream)").unwrap();
454 writeln!(writer, "companion object {{").unwrap();
455 writer.inc_ident();
456 writeln!(writer, "@Throws(java.io.IOException::class)").unwrap();
457 writeln!(
458 writer,
459 "fun readFrom(stream: java.io.InputStream): {} {{",
460 base_name.camel_case(conv)
461 )
462 .unwrap();
463 {
464 writer.inc_ident();
465 writeln!(writer, "when (StreamUtil.readInt(stream)) {{").unwrap();
466 writer.inc_ident();
467 for variant in variants {
468 write!(writer, "{}.TAG -> ", variant.name.camel_case(conv)).unwrap();
469 writeln!(
470 writer,
471 "return {}.readFrom(stream)",
472 variant.name.camel_case(conv)
473 )
474 .unwrap();
475 }
476 writeln!(
477 writer,
478 "else -> throw java.io.IOException(\"Unexpected discriminant value\")"
479 )
480 .unwrap();
481 writer.dec_ident();
482 writeln!(writer, "}}").unwrap();
483 writer.dec_ident();
484 }
485 writeln!(writer, "}}").unwrap();
486 writer.dec_ident();
487 writeln!(writer, "}}").unwrap();
488 for (discriminant, variant) in variants.iter().enumerate() {
489 writeln!(writer).unwrap();
490 write_struct(&mut writer, variant, Some((base_name, discriminant)))
491 .unwrap();
492 }
493 writer.dec_ident();
494 }
495 writeln!(writer, "}}").unwrap();
496 self.files.insert(file_name, writer.get());
497 }
498 Schema::Bool
499 | Schema::Int32
500 | Schema::Int64
501 | Schema::Float32
502 | Schema::Float64
503 | Schema::String
504 | Schema::Option(_)
505 | Schema::Vec(_)
506 | Schema::Map(_, _) => {}
507 }
508 }
509}