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!("Option[{}]", type_name(inner)),
34 Schema::Vec(inner) => format!("Seq[{}]", type_name(inner)),
35 Schema::Map(key, value) => format!("Map[{}, {}]", type_name(key), type_name(value)),
36 }
37}
38
39fn write_struct(
40 writer: &mut Writer,
41 struc: &Struct,
42 base: Option<(&Name, usize)>,
43) -> std::fmt::Result {
44 write!(writer, "case class {}(", struc.name.camel_case(conv))?;
46 for (index, field) in struc.fields.iter().enumerate() {
47 if index > 0 {
48 write!(writer, ", ")?;
49 }
50 write!(
51 writer,
52 "{}: {}",
53 field.name.mixed_case(conv),
54 type_name(&field.schema),
55 )?;
56 }
57 write!(writer, ")")?;
58 if let Some((base, _)) = base {
59 write!(writer, " extends {}", base.camel_case(conv))?;
60 }
61 writeln!(writer, " {{")?;
62 writer.inc_ident();
63
64 if base.is_some() {
66 write!(writer, "override ")?;
67 }
68 writeln!(writer, "def writeTo(stream: java.io.OutputStream) {{")?;
69 writer.inc_ident();
70 if base.is_some() {
71 writeln!(
72 writer,
73 "StreamUtil.writeInt(stream, {}.TAG)",
74 struc.name.camel_case(conv),
75 )?;
76 }
77 if let Some(magic) = struc.magic {
78 writeln!(writer, "StreamUtil.writeInt(stream, {})", magic)?;
79 }
80 for field in &struc.fields {
81 fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
82 match schema {
83 Schema::Bool => {
84 writeln!(writer, "StreamUtil.writeBoolean(stream, {})", value)?;
85 }
86 Schema::Int32 => {
87 writeln!(writer, "StreamUtil.writeInt(stream, {})", value)?;
88 }
89 Schema::Int64 => {
90 writeln!(writer, "StreamUtil.writeLong(stream, {})", value)?;
91 }
92 Schema::Float32 => {
93 writeln!(writer, "StreamUtil.writeFloat(stream, {})", value)?;
94 }
95 Schema::Float64 => {
96 writeln!(writer, "StreamUtil.writeDouble(stream, {})", value)?;
97 }
98 Schema::String => {
99 writeln!(writer, "StreamUtil.writeString(stream, {})", value)?;
100 }
101 Schema::Struct(_) | Schema::OneOf { .. } | Schema::Enum { .. } => {
102 writeln!(writer, "{}.writeTo(stream)", value)?;
103 }
104 Schema::Option(inner) => {
105 writeln!(writer, "{} match {{", value)?;
106 writer.inc_ident();
107 writeln!(
108 writer,
109 "case None => StreamUtil.writeBoolean(stream, false)"
110 )?;
111 writeln!(writer, "case Some(value) => {{")?;
112 writer.inc_ident();
113 writeln!(writer, "StreamUtil.writeBoolean(stream, true)")?;
114 write(writer, "value", inner)?;
115 writer.dec_ident();
116 writeln!(writer, "}}")?;
117 writer.dec_ident();
118 writeln!(writer, "}}")?;
119 }
120 Schema::Vec(inner) => {
121 writeln!(writer, "StreamUtil.writeInt(stream, {}.length)", value)?;
122 writeln!(writer, "{}.foreach {{ value =>", value)?;
123 writer.inc_ident();
124 write(writer, "value", inner)?;
125 writer.dec_ident();
126 writeln!(writer, "}}")?;
127 }
128 Schema::Map(key_type, value_type) => {
129 writeln!(writer, "StreamUtil.writeInt(stream, {}.size)", value)?;
130 writeln!(writer, "{}.foreach {{ case (key, value) =>", value)?;
131 writer.inc_ident();
132 write(writer, "key", key_type)?;
133 write(writer, "value", value_type)?;
134 writer.dec_ident();
135 writeln!(writer, "}}")?;
136 }
137 }
138 Ok(())
139 }
140 write(writer, &field.name.mixed_case(conv), &field.schema)?;
141 }
142 writer.dec_ident();
143 writeln!(writer, "}}")?;
144
145 writer.dec_ident();
146 writeln!(writer, "}}")?;
147
148 writeln!(writer, "object {} {{", struc.name.camel_case(conv))?;
150 writer.inc_ident();
151 if let Some((_, discriminant)) = base {
152 writeln!(writer, "val TAG: Int = {}", discriminant)?;
153 }
154
155 writeln!(
157 writer,
158 "def readFrom(stream: java.io.InputStream): {} = {}(",
159 struc.name.camel_case(conv),
160 struc.name.camel_case(conv),
161 )?;
162 writer.inc_ident();
163 for (index, field) in struc.fields.iter().enumerate() {
164 fn read(writer: &mut Writer, schema: &Schema) -> std::fmt::Result {
165 match schema {
166 Schema::Bool => {
167 writeln!(writer, "StreamUtil.readBoolean(stream)")?;
168 }
169 Schema::Int32 => {
170 writeln!(writer, "StreamUtil.readInt(stream)")?;
171 }
172 Schema::Int64 => {
173 writeln!(writer, "StreamUtil.readLong(stream)")?;
174 }
175 Schema::Float32 => {
176 writeln!(writer, "StreamUtil.readFloat(stream)")?;
177 }
178 Schema::Float64 => {
179 writeln!(writer, "StreamUtil.readDouble(stream)")?;
180 }
181 Schema::String => {
182 writeln!(writer, "StreamUtil.readString(stream)")?;
183 }
184 Schema::Struct(Struct { name, .. })
185 | Schema::OneOf {
186 base_name: name, ..
187 }
188 | Schema::Enum {
189 base_name: name, ..
190 } => {
191 writeln!(writer, "model.{}.readFrom(stream)", name.camel_case(conv))?;
192 }
193 Schema::Option(inner) => {
194 writeln!(writer, "if (StreamUtil.readBoolean(stream)) Some(")?;
195 writer.inc_ident();
196 read(writer, inner)?;
197 writer.dec_ident();
198 writeln!(writer, ") else None")?;
199 }
200 Schema::Vec(inner) => {
201 writeln!(writer, "(0 until StreamUtil.readInt(stream)).map {{ _ =>",)?;
202 writer.inc_ident();
203 read(writer, inner)?;
204 writer.dec_ident();
205 writeln!(writer, "}}")?;
206 }
207 Schema::Map(key_type, value_type) => {
208 writeln!(writer, "(0 until StreamUtil.readInt(stream)).map {{ _ => (",)?;
209 writer.inc_ident();
210 read(writer, key_type)?;
211 writeln!(writer, ",")?;
212 read(writer, value_type)?;
213 writer.dec_ident();
214 writeln!(writer, ")}}.toMap")?;
215 }
216 }
217 Ok(())
218 }
219 read(writer, &field.schema)?;
220 if index + 1 < struc.fields.len() {
221 writeln!(writer, ",")?;
222 }
223 }
224 writeln!(writer, ")")?;
225 writer.dec_ident();
226 writer.dec_ident();
227 writeln!(writer, "}}")?;
228 Ok(())
229}
230
231impl trans_gen_core::Generator for Generator {
232 fn new(name: &str, version: &str) -> Self {
233 let mut files = HashMap::new();
234 files.insert(
235 "util/StreamUtil.scala".to_owned(),
236 include_str!("../template/StreamUtil.scala").to_owned(),
237 );
238 Self { files }
239 }
240 fn result(self) -> HashMap<String, String> {
241 self.files
242 }
243 fn add_only(&mut self, schema: &Schema) {
244 match schema {
245 Schema::Enum {
246 base_name,
247 variants,
248 } => {
249 let file_name = format!("model/{}.scala", base_name.camel_case(conv));
250 let mut writer = Writer::new();
251 writeln!(writer, "package model").unwrap();
252 writeln!(writer).unwrap();
253 writeln!(writer, "import util.StreamUtil").unwrap();
254 writeln!(writer).unwrap();
255 writeln!(
256 writer,
257 "sealed abstract class {} (val discriminant: Int) {{",
258 base_name.camel_case(conv)
259 )
260 .unwrap();
261 writer.inc_ident();
262 writeln!(writer, "def writeTo(stream: java.io.OutputStream) {{").unwrap();
263 writeln!(writer, " StreamUtil.writeInt(stream, discriminant)").unwrap();
264 writeln!(writer, "}}").unwrap();
265 writer.dec_ident();
266 writeln!(writer, "}}").unwrap();
267 writeln!(writer).unwrap();
268 writeln!(writer, "object {} {{", base_name.camel_case(conv)).unwrap();
269 writer.inc_ident();
270 for (index, variant) in variants.iter().enumerate() {
271 writeln!(
272 writer,
273 "case object {} extends {}({})",
274 variant.shouty_snake_case(conv),
275 base_name.camel_case(conv),
276 index,
277 )
278 .unwrap();
279 }
280 writeln!(
281 writer,
282 "def readFrom(stream: java.io.InputStream): {} = StreamUtil.readInt(stream) match {{",
283 base_name.camel_case(conv)
284 )
285 .unwrap();
286 writer.inc_ident();
287 for (index, variant) in variants.iter().enumerate() {
288 writeln!(
289 writer,
290 "case {} => {}",
291 index,
292 variant.shouty_snake_case(conv),
293 )
294 .unwrap();
295 }
296 writeln!(
297 writer,
298 "case _ => throw new java.io.IOException(\"Unexpected discriminant value\")"
299 )
300 .unwrap();
301 writer.dec_ident();
302 writeln!(writer, "}}").unwrap();
303 writer.dec_ident();
304 writeln!(writer, "}}").unwrap();
305 self.files.insert(file_name, writer.get());
306 }
307 Schema::Struct(struc) => {
308 let file_name = format!("model/{}.scala", struc.name.camel_case(conv));
309 let mut writer = Writer::new();
310 writeln!(writer, "package model").unwrap();
311 writeln!(writer).unwrap();
312 writeln!(writer, "import util.StreamUtil").unwrap();
313 writeln!(writer).unwrap();
314 write_struct(&mut writer, struc, None).unwrap();
315 self.files.insert(file_name, writer.get());
316 }
317 Schema::OneOf {
318 base_name,
319 variants,
320 } => {
321 let file_name = format!("model/{}.scala", base_name.camel_case(conv));
322 let mut writer = Writer::new();
323 writeln!(writer, "package model").unwrap();
324 writeln!(writer).unwrap();
325 writeln!(writer, "import util.StreamUtil").unwrap();
326 writeln!(writer).unwrap();
327 writeln!(writer, "sealed trait {} {{", base_name.camel_case(conv)).unwrap();
328 writeln!(writer, " def writeTo(stream: java.io.OutputStream)").unwrap();
329 writeln!(writer, "}}").unwrap();
330 writeln!(&mut writer, "object {} {{", base_name.camel_case(conv)).unwrap();
331 {
332 writer.inc_ident();
333 for (discriminant, variant) in variants.iter().enumerate() {
334 write_struct(&mut writer, variant, Some((base_name, discriminant)))
335 .unwrap();
336 writeln!(&mut writer).unwrap();
337 }
338 writeln!(
339 &mut writer,
340 "def readFrom(stream: java.io.InputStream): {} = {{",
341 base_name.camel_case(conv)
342 )
343 .unwrap();
344 {
345 writer.inc_ident();
346 writeln!(&mut writer, "StreamUtil.readInt(stream) match {{").unwrap();
347 writer.inc_ident();
348 for variant in variants {
349 writeln!(
350 &mut writer,
351 "case {}.TAG => {}.readFrom(stream)",
352 variant.name.camel_case(conv),
353 variant.name.camel_case(conv),
354 )
355 .unwrap();
356 }
357 writeln!(
358 &mut writer,
359 "case _ => throw new java.io.IOException(\"Unexpected discriminant value\")"
360 )
361 .unwrap();
362 writer.dec_ident();
363 writeln!(&mut writer, "}}").unwrap();
364 writer.dec_ident();
365 }
366 writeln!(&mut writer, "}}").unwrap();
367 writer.dec_ident();
368 }
369 writeln!(&mut writer, "}}").unwrap();
370 self.files.insert(file_name, writer.get());
371 }
372 Schema::Bool
373 | Schema::Int32
374 | Schema::Int64
375 | Schema::Float32
376 | Schema::Float64
377 | Schema::String
378 | Schema::Option(_)
379 | Schema::Vec(_)
380 | Schema::Map(_, _) => {}
381 }
382 }
383}