trans_gen/gens/fsharp/
mod.rs

1use super::*;
2
3fn conv(name: &str) -> String {
4    name.replace("Int32", "Int")
5        .replace("Int64", "Long")
6        .replace("Float32", "Single")
7        .replace("Float64", "Double")
8        .replace("Params", "Parameters")
9}
10
11pub struct Generator {
12    main_namespace: String,
13    files: Vec<File>,
14}
15
16fn type_name(schema: &Schema) -> String {
17    format!("{}{}", type_name_prearray(schema), type_post_array(schema))
18}
19
20fn type_name_prearray(schema: &Schema) -> String {
21    match schema {
22        Schema::Bool => "bool".to_owned(),
23        Schema::Int32 => "int".to_owned(),
24        Schema::Int64 => "long".to_owned(),
25        Schema::Float32 => "single".to_owned(),
26        Schema::Float64 => "double".to_owned(),
27        Schema::String => "string".to_owned(),
28        Schema::Struct(Struct { name, .. })
29        | Schema::OneOf {
30            base_name: name, ..
31        }
32        | Schema::Enum {
33            base_name: name, ..
34        } => format!("{}", name.camel_case(conv)),
35        Schema::Option(inner) => format!("option<{}>", type_name(inner)),
36        Schema::Vec(inner) => type_name_prearray(inner),
37        Schema::Map(key, value) => format!("Map<{}, {}>", type_name(key), type_name(value)),
38    }
39}
40
41fn type_post_array(schema: &Schema) -> String {
42    match schema {
43        Schema::Vec(inner) => format!("[]{}", type_post_array(inner)),
44        _ => String::new(),
45    }
46}
47
48fn var_name(name: &str) -> &str {
49    match name.rfind('.') {
50        Some(index) => &name[(index + 1)..],
51        None => name,
52    }
53}
54
55fn write_struct(
56    writer: &mut Writer,
57    struc: &Struct,
58    base: Option<(&Name, usize)>,
59) -> std::fmt::Result {
60    let struc_name = if let Some((base, _)) = base {
61        format!("{}{}", base.camel_case(conv), struc.name.camel_case(conv))
62    } else {
63        struc.name.camel_case(conv)
64    };
65
66    if struc.fields.is_empty() {
67        writeln!(writer, "type {} = struct end with", struc_name)?;
68        writer.inc_ident();
69    } else {
70        writeln!(writer, "type {} = {{", struc_name)?;
71        writer.inc_ident();
72        for field in &struc.fields {
73            writeln!(
74                writer,
75                "{}: {};",
76                field.name.camel_case(conv),
77                type_name(&field.schema),
78            )?;
79        }
80        writeln!(writer, "}} with")?;
81    }
82
83    // Writing
84    writeln!(
85        writer,
86        "member this.writeTo(writer: System.IO.BinaryWriter) ="
87    )?;
88    writer.inc_ident();
89    if let Some((_, tag)) = base {
90        writeln!(writer, "writer.Write {}", tag)?;
91    }
92    if let Some(magic) = struc.magic {
93        writeln!(writer, "writer.Write {}", magic)?;
94    }
95    for field in &struc.fields {
96        fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
97            match schema {
98                Schema::Bool => {
99                    writeln!(writer, "writer.Write {}", value)?;
100                }
101                Schema::Int32 => {
102                    writeln!(writer, "writer.Write {}", value)?;
103                }
104                Schema::Int64 => {
105                    writeln!(writer, "writer.Write {}", value)?;
106                }
107                Schema::Float32 => {
108                    writeln!(writer, "writer.Write {}", value)?;
109                }
110                Schema::Float64 => {
111                    writeln!(writer, "writer.Write {}", value)?;
112                }
113                Schema::String => {
114                    let data_var = format!("{}Data", var_name(value));
115                    writeln!(
116                        writer,
117                        "let {} : byte[] = System.Text.Encoding.UTF8.GetBytes {}",
118                        data_var, value
119                    )?;
120                    writeln!(writer, "writer.Write {}.Length", data_var)?;
121                    writeln!(writer, "writer.Write {}", data_var)?;
122                }
123                Schema::Struct(_) | Schema::OneOf { .. } => {
124                    writeln!(writer, "{}.writeTo writer", value)?;
125                }
126                Schema::Option(inner) => {
127                    writeln!(writer, "match {} with", value)?;
128                    writer.inc_ident();
129                    writeln!(writer, "| Some value ->")?;
130                    writer.inc_ident();
131                    writeln!(writer, "writer.Write true")?;
132                    write(writer, "value", inner)?;
133                    writer.dec_ident();
134                    writeln!(writer, "| None -> writer.Write false")?;
135                    writer.dec_ident();
136                }
137                Schema::Vec(inner) => {
138                    writeln!(writer, "writer.Write {}.Length", value)?;
139                    writeln!(writer, "{} |> Array.iter (fun value ->", value)?;
140                    writer.inc_ident();
141                    write(writer, "value", inner)?;
142                    writer.dec_ident();
143                    writeln!(writer, ")")?;
144                }
145                Schema::Map(key_type, value_type) => {
146                    writeln!(writer, "writer.Write {}.Count", value)?;
147                    writeln!(writer, "{} |> Map.iter (fun key value ->", value)?;
148                    writer.inc_ident();
149                    write(writer, "key", key_type)?;
150                    write(writer, "value", value_type)?;
151                    writer.dec_ident();
152                    writeln!(writer, ")")?;
153                }
154                Schema::Enum { .. } => {
155                    writeln!(writer, "writer.Write (int {})", value)?;
156                }
157            }
158            Ok(())
159        }
160        write(
161            writer,
162            &format!("this.{}", field.name.camel_case(conv)),
163            &field.schema,
164        )?;
165    }
166    writer.dec_ident();
167
168    // Reading
169    if struc.fields.is_empty() {
170        writeln!(
171            writer,
172            "static member readFrom(reader: System.IO.BinaryReader) = new {}()",
173            struc_name,
174        )?;
175    } else {
176        writeln!(
177            writer,
178            "static member readFrom(reader: System.IO.BinaryReader) = {{"
179        )?;
180        writer.inc_ident();
181        for field in &struc.fields {
182            fn read(writer: &mut Writer, schema: &Schema) -> std::fmt::Result {
183                match schema {
184                    Schema::Bool => {
185                        writeln!(writer, "reader.ReadBoolean()")?;
186                    }
187                    Schema::Int32 => {
188                        writeln!(writer, "reader.ReadInt32()")?;
189                    }
190                    Schema::Int64 => {
191                        writeln!(writer, "reader.ReadInt64()")?;
192                    }
193                    Schema::Float32 => {
194                        writeln!(writer, "reader.ReadSingle()")?;
195                    }
196                    Schema::Float64 => {
197                        writeln!(writer, "reader.ReadDouble()")?;
198                    }
199                    Schema::String => {
200                        writeln!(writer, "reader.ReadInt32() |> reader.ReadBytes |> System.Text.Encoding.UTF8.GetString")?;
201                    }
202                    Schema::Struct(Struct { name, .. })
203                    | Schema::OneOf {
204                        base_name: name, ..
205                    } => {
206                        writeln!(writer, "{}.readFrom reader", name.camel_case(conv))?;
207                    }
208                    Schema::Option(inner) => {
209                        writeln!(writer, "match reader.ReadBoolean() with")?;
210                        writer.inc_ident();
211                        writeln!(writer, "| true ->")?;
212                        writer.inc_ident();
213                        writeln!(writer, "Some(")?;
214                        writer.inc_ident();
215                        read(writer, inner)?;
216                        writeln!(writer, ")")?;
217                        writer.dec_ident();
218                        writer.dec_ident();
219                        writeln!(writer, "| false -> None")?;
220                        writer.dec_ident();
221                    }
222                    Schema::Vec(inner) => {
223                        writeln!(writer, "[|for _ in 1 .. reader.ReadInt32() do")?;
224                        writer.inc_ident();
225                        write!(writer, "yield ")?;
226                        read(writer, inner)?;
227                        writer.dec_ident();
228                        writeln!(writer, "|]")?;
229                    }
230                    Schema::Map(key_type, value_type) => {
231                        writeln!(writer, "[for _ in 1 .. reader.ReadInt32() do")?;
232                        writer.inc_ident();
233                        write!(writer, "let key = ")?;
234                        read(writer, key_type)?;
235                        write!(writer, "let value = ")?;
236                        read(writer, value_type)?;
237                        writeln!(writer, "yield (key, value)")?;
238                        writeln!(writer, "] |> Map.ofList")?;
239                        writer.dec_ident();
240                    }
241                    Schema::Enum { .. } => {
242                        writeln!(writer, "reader.ReadInt32() |> enum")?;
243                    }
244                }
245                Ok(())
246            }
247            write!(writer, "{} = ", field.name.camel_case(conv))?;
248            read(writer, &field.schema)?;
249        }
250        writer.dec_ident();
251        writeln!(writer, "}}")?;
252    }
253
254    writer.dec_ident();
255
256    Ok(())
257}
258
259impl crate::Generator for Generator {
260    type Options = ();
261    fn new(name: &str, _version: &str, _: ()) -> Self {
262        Self {
263            main_namespace: Name::new(name.to_owned()).camel_case(conv),
264            files: Vec::new(),
265        }
266    }
267    fn result(self) -> GenResult {
268        self.files.into()
269    }
270    fn add_only(&mut self, schema: &Schema) {
271        match schema {
272            Schema::Enum {
273                documentation: _,
274                base_name,
275                variants,
276            } => {
277                let file_name = format!("Model/{}.fs", base_name.camel_case(conv));
278                let mut writer = Writer::new();
279                writeln!(writer, "#nowarn \"0058\"").unwrap();
280                writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
281                writeln!(writer, "type {} =", base_name.camel_case(conv)).unwrap();
282                writer.inc_ident();
283                for (tag, variant) in variants.iter().enumerate() {
284                    writeln!(writer, "| {} = {}", variant.name.camel_case(conv), tag).unwrap();
285                }
286                writer.dec_ident();
287                self.files.push(File {
288                    path: file_name,
289                    content: writer.get(),
290                });
291            }
292            Schema::Struct(struc) => {
293                let file_name = format!("Model/{}.fs", struc.name.camel_case(conv));
294                let mut writer = Writer::new();
295                writeln!(writer, "#nowarn \"0058\"").unwrap();
296                writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
297                write_struct(&mut writer, struc, None).unwrap();
298                self.files.push(File {
299                    path: file_name,
300                    content: writer.get(),
301                });
302            }
303            Schema::OneOf {
304                documentation: _,
305                base_name,
306                variants,
307            } => {
308                let file_name = format!("Model/{}.fs", base_name.camel_case(conv));
309                let mut writer = Writer::new();
310                writeln!(writer, "#nowarn \"0058\"").unwrap();
311                writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
312
313                for (tag, variant) in variants.iter().enumerate() {
314                    writeln!(&mut writer).unwrap();
315                    write_struct(&mut writer, variant, Some((base_name, tag))).unwrap();
316                }
317
318                writeln!(&mut writer, "type {} = ", base_name.camel_case(conv)).unwrap();
319                writer.inc_ident();
320                for variant in variants {
321                    writeln!(
322                        writer,
323                        "| {} of {}{}",
324                        variant.name.camel_case(conv),
325                        base_name.camel_case(conv),
326                        variant.name.camel_case(conv)
327                    )
328                    .unwrap();
329                }
330                writeln!(writer, "with").unwrap();
331
332                writeln!(
333                    writer,
334                    "member this.writeTo(writer: System.IO.BinaryWriter) ="
335                )
336                .unwrap();
337                writer.inc_ident();
338                writeln!(writer, "match this with").unwrap();
339                writer.inc_ident();
340                for variant in variants {
341                    writeln!(
342                        writer,
343                        "| {} value -> value.writeTo writer",
344                        variant.name.camel_case(conv),
345                    )
346                    .unwrap();
347                }
348                writer.dec_ident();
349                writer.dec_ident();
350
351                writeln!(
352                    writer,
353                    "static member readFrom(reader: System.IO.BinaryReader) ="
354                )
355                .unwrap();
356                writer.inc_ident();
357                writeln!(writer, "match reader.ReadInt32() with").unwrap();
358                writer.inc_ident();
359                for (tag, variant) in variants.iter().enumerate() {
360                    writeln!(
361                        writer,
362                        "| {} -> {} ({}{}.readFrom reader)",
363                        tag,
364                        variant.name.camel_case(conv),
365                        base_name.camel_case(conv),
366                        variant.name.camel_case(conv),
367                    )
368                    .unwrap();
369                }
370                writeln!(writer, "| x -> failwith (sprintf \"Unexpected tag %d\" x)").unwrap();
371                writer.dec_ident();
372                writer.dec_ident();
373
374                writer.dec_ident();
375                self.files.push(File {
376                    path: file_name,
377                    content: writer.get(),
378                });
379            }
380            Schema::Bool
381            | Schema::Int32
382            | Schema::Int64
383            | Schema::Float32
384            | Schema::Float64
385            | Schema::String
386            | Schema::Option(_)
387            | Schema::Vec(_)
388            | Schema::Map(_, _) => {}
389        }
390    }
391}