1use itertools::Itertools;
2
3use crate::{
4 builder::TypesBuilder,
5 kind::{BuiltinTypeKind, Field, TagType, TypeKind, VariantKind},
6 DynTapi,
7};
8
9pub fn builder() -> TypesBuilder {
10 TypesBuilder {
11 prelude: include_str!("./prelude.fs").to_string() + "\n",
12 start_namespace: Box::new(|_, name| format!("module {name} =")),
13 end_namespace: Box::new(|_, _| String::new()),
14 decl: Box::new(ty_decl),
15 }
16}
17
18pub fn full_ty_name(ty: DynTapi) -> String {
19 let mut name = ty_name(ty);
20 for p in ty.path().iter().rev() {
21 name = format!("{}.{}", p, name);
22 }
23 name
24}
25
26pub fn ty_name(ty: DynTapi) -> String {
27 use BuiltinTypeKind::*;
28
29 match ty.kind() {
30 TypeKind::Struct(s) => s.attr.name.serialize_name,
31 TypeKind::TupleStruct(s) => s.attr.name.serialize_name,
32 TypeKind::Enum(e) => e.attr.name.serialize_name,
33 TypeKind::List(ty) => format!("List<{}>", full_ty_name(ty)),
34 TypeKind::Option(ty) => format!("Option<{}>", full_ty_name(ty)),
35 TypeKind::Tuple(fields) => fs_tuple(&fields),
36 TypeKind::Record(k, v) => format!("Map<{}, {}>", full_ty_name(k), full_ty_name(v)),
37 TypeKind::Any => "any".to_string(),
38 TypeKind::Builtin(b) => match b {
39 U8 => "uint8",
40 U16 => "uint16",
41 U32 => "uint32",
42 U64 => "uint64",
43 U128 => "uint128",
44 I8 => "int8",
45 I16 => "int16",
46 I32 => "int32",
47 I64 => "int64",
48 I128 => "int128",
49 F32 => "float32",
50 F64 => "float",
51 Usize => "uint",
52 Isize => "int",
53 Bool => "bool",
54 Char => "char",
55 String => "string",
56 Unit => "unit",
57 }
58 .to_string(),
59 }
60}
61
62pub fn ty_decl(ty: DynTapi) -> Option<String> {
63 use std::fmt::Write;
64 fn inner(ty: DynTapi) -> Result<Option<String>, std::fmt::Error> {
65 Ok(Some(match ty.kind() {
66 TypeKind::Struct(s) => {
67 let fs_fields = fs_fields(&s.fields);
68 format!("type {} =\n {{ {fs_fields} }}", s.attr.name.serialize_name)
69 }
70 TypeKind::TupleStruct(s) => {
71 let fs_fields = fs_tuple(&s.fields.iter().map(|f| f.ty).collect_vec());
72 format!("type {} = {fs_fields}", s.attr.name.serialize_name)
73 }
74 TypeKind::Enum(e) => {
75 let mut out = String::new();
76 let encoding = [
77 "JsonUnionEncoding.ExternalTag",
78 "JsonUnionEncoding.UnwrapFieldlessTags",
79 "JsonUnionEncoding.UnwrapSingleFieldCases",
80 ];
81
82 let converter_options = match &e.attr.tag {
83 TagType::External => vec![format!(
84 "BaseUnionEncoding = {}",
85 encoding.iter().format(" + ")
86 )],
87 TagType::Internal { tag } => vec![
88 format!(
89 "BaseUnionEncoding = {}",
90 ["JsonUnionEncoding.UnwrapSingleFieldCases"]
91 .iter()
92 .format(" + ")
93 ),
94 format!("UnionTagName = {tag:?}"),
95 ],
96 TagType::Adjacent { tag, content } => vec![
97 format!(
98 "BaseUnionEncoding = {}",
99 ["JsonUnionEncoding.UnwrapSingleFieldCases"]
100 .iter()
101 .format(" + ")
102 ),
103 format!("UnionTagName = {tag:?}"),
104 format!("UnionFieldsName = {content:?}"),
105 ],
106 TagType::None => vec![format!(
107 "BaseUnionEncoding = {}",
108 encoding.iter().format(" + ")
109 )],
110 };
111
112 writeln!(
113 out,
114 "[<JsonFSharpConverter({})>]",
115 converter_options.iter().format(", "),
116 )?;
117 writeln!(out, "type {} =", e.attr.name.serialize_name)?;
118
119 for v in &e.variants {
120 match &v.kind {
121 VariantKind::Unit => writeln!(out, " | {}", v.name)?,
122 VariantKind::Tuple(fields) => {
123 writeln!(out, " | {} of {}", v.name, fs_tuple(fields))?
124 }
125 VariantKind::Struct(fields) => {
126 writeln!(out, " | {} of {}", v.name, fs_named_tuple(fields))?
127 }
128 }
129 }
130
131 out
132 }
133 TypeKind::List(_)
134 | TypeKind::Option(_)
135 | TypeKind::Tuple(_)
136 | TypeKind::Record(_, _)
137 | TypeKind::Any
138 | TypeKind::Builtin(_) => return Ok(None),
139 }))
140 }
141 inner(ty).unwrap()
142}
143
144fn fs_tuple(fields: &[DynTapi]) -> String {
145 format!("{}", fields.iter().map(|f| full_ty_name(*f)).format(" * "))
146}
147
148fn fs_named_tuple(fields: &[Field]) -> String {
149 format!(
150 "{}",
151 fields
152 .iter()
153 .map(|f| format!("{}: {}", f.attr.name.serialize_name, full_ty_name(f.ty)))
154 .format(" * ")
155 )
156}
157
158fn fs_fields(fields: &[crate::kind::Field]) -> impl std::fmt::Display + '_ {
159 fields
160 .iter()
161 .filter(|f| !f.attr.skip_serializing)
162 .map(|f| {
163 let name = match &f.name {
164 crate::kind::FieldName::Named(n) => &n.serialize_name,
165 crate::kind::FieldName::Index(_) => todo!(),
166 };
167 format!("{name}: {}", full_ty_name(f.ty))
168 })
169 .format("\n ")
170}