tapi/targets/
fs.rs

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}