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