trans_gen_fsharp/
lib.rs

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