@tutao/licc 3.98.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,241 @@
1
+ import {FacadeDefinition, getArgs, LangGenerator, MethodDefinition, minusculize, RenderedType, StructDefinition, TypeRefDefinition} from "./common.js"
2
+ import {Accumulator} from "./Accumulator.js"
3
+ import {ParsedType, parseType} from "./Parser.js"
4
+
5
+ export class KotlinGenerator implements LangGenerator {
6
+
7
+ generateGlobalDispatcher(name: string, facadeNames: string[]): string {
8
+ const acc = new Accumulator()
9
+ KotlinGenerator.generateImports(acc)
10
+ acc.line(`import de.tutao.tutanota.ipc.*`)
11
+ acc.line()
12
+ acc.line(`class ${name} (`)
13
+ const methodAcc = acc.indent()
14
+ methodAcc.line(`json: Json,`)
15
+ for (let facadeName of facadeNames) {
16
+ methodAcc.line(`${minusculize(facadeName)} : ${facadeName},`)
17
+ }
18
+ acc.line(") {")
19
+ for (let facadeName of facadeNames) {
20
+ methodAcc.line(`private val ${minusculize(facadeName)}: ${facadeName}ReceiveDispatcher = ${facadeName}ReceiveDispatcher(json, ${minusculize(facadeName)})`)
21
+ }
22
+ methodAcc.line()
23
+
24
+ methodAcc.line(`suspend fun dispatch(facadeName: String, methodName: String, args: List<String>): String {`)
25
+ const whenAcc = methodAcc.indent()
26
+ whenAcc.line(`return when (facadeName) {`)
27
+ const caseAcc = whenAcc.indent()
28
+ for (let facadeName of facadeNames) {
29
+ caseAcc.line(`"${facadeName}" -> this.${minusculize(facadeName)}.dispatch(methodName, args)`)
30
+ }
31
+ caseAcc.line(`else -> throw Error("unknown facade: $facadeName")`)
32
+ whenAcc.line(`}`)
33
+ methodAcc.line(`}`)
34
+ acc.line(`}`)
35
+ return acc.finish()
36
+ }
37
+
38
+ handleStructDefinition(definition: StructDefinition): string {
39
+ const acc = new Accumulator()
40
+ KotlinGenerator.generateImports(acc)
41
+ acc.line()
42
+ if (definition.doc) {
43
+ this.generateDocComment(acc, definition.doc)
44
+ }
45
+ acc.line("@Serializable")
46
+ acc.line(`data class ${definition.name}(`)
47
+ const fieldGenerator = acc.indent()
48
+ for (const [name, fieldDefinition] of Object.entries(definition.fields)) {
49
+ const renderedType = typeNameKotlin(fieldDefinition)
50
+ fieldGenerator.line(`val ${name}: ${renderedType.name},`)
51
+ }
52
+ acc.line(")")
53
+ return acc.finish()
54
+ }
55
+
56
+ private static generateImports(acc: Accumulator) {
57
+ acc.line("package de.tutao.tutanota.ipc")
58
+ acc.line()
59
+ acc.line("import kotlinx.serialization.*")
60
+ acc.line("import kotlinx.serialization.json.*")
61
+ acc.line()
62
+ }
63
+
64
+ private generateDocComment(acc: Accumulator, comment: string | null | undefined) {
65
+ if (!comment) return
66
+ acc.line("/**")
67
+ acc.line(` * ${comment}`)
68
+ acc.line(" */")
69
+ }
70
+
71
+ generateFacade(definition: FacadeDefinition): string {
72
+ const acc = new Accumulator()
73
+ KotlinGenerator.generateImports(acc)
74
+ this.generateDocComment(acc, definition.doc)
75
+ acc.line(`interface ${definition.name} {`)
76
+ const methodAcc = acc.indent()
77
+ for (const [name, methodDefinition] of Object.entries(definition.methods)) {
78
+ this.generateDocComment(methodAcc, methodDefinition.doc)
79
+ KotlinGenerator.generateMethodSignature(methodAcc, name, methodDefinition)
80
+ }
81
+ acc.line("}")
82
+ return acc.finish()
83
+ }
84
+
85
+ generateReceiveDispatcher(definition: FacadeDefinition): string {
86
+ const acc = new Accumulator()
87
+ // Some names might clash, we don't read this file, we don't care
88
+ acc.line(`@file:Suppress("NAME_SHADOWING")`)
89
+ KotlinGenerator.generateImports(acc)
90
+ acc.line(`class ${definition.name}ReceiveDispatcher(`)
91
+ acc.indent().line(`private val json: Json,`)
92
+ acc.indent().line(`private val facade: ${definition.name},`)
93
+ acc.line(`) {`)
94
+ const methAcc = acc.indent()
95
+ methAcc.line()
96
+ methAcc.line(`suspend fun dispatch(method: String, arg: List<String>): String {`)
97
+ const whenAcc = methAcc.indent()
98
+ whenAcc.line(`when (method) {`)
99
+ const caseAcc = whenAcc.indent()
100
+ for (const [methodName, methodDef] of Object.entries(definition.methods)) {
101
+ caseAcc.line(`"${methodName}" -> {`)
102
+ const arg = getArgs(methodName, methodDef)
103
+ const decodedArgs = []
104
+ for (let i = 0; i < arg.length; i++) {
105
+ const {name: argName, type} = arg[i]
106
+ const renderedArgType = typeNameKotlin(type)
107
+ decodedArgs.push([argName, renderedArgType] as const)
108
+ }
109
+ const varAcc = caseAcc.indent()
110
+ for (let i = 0; i < arg.length; i++) {
111
+ const [argName, renderedType] = decodedArgs[i]
112
+ varAcc.line(`val ${argName}: ${renderedType.name} = json.decodeFromString(arg[${i}])`)
113
+ }
114
+ varAcc.line(`val result: ${typeNameKotlin(methodDef.ret).name} = this.facade.${methodName}(`)
115
+ for (let i = 0; i < arg.length; i++) {
116
+ const [argName] = decodedArgs[i]
117
+ varAcc.indent().line(`${argName},`)
118
+ }
119
+ varAcc.line(`)`)
120
+ varAcc.line(`return json.encodeToString(result)`)
121
+ caseAcc.line(`}`)
122
+ }
123
+ caseAcc.line(`else -> throw Error("unknown method for ${definition.name}: $method")`)
124
+ whenAcc.line(`}`)
125
+ methAcc.line(`}`)
126
+ acc.line(`}`)
127
+ return acc.finish()
128
+ }
129
+
130
+ private static generateNativeInterface(): string {
131
+ const acc = new Accumulator()
132
+ KotlinGenerator.generateImports(acc)
133
+ acc.line(`interface NativeInterface {`)
134
+ acc.indent().line(`suspend fun sendRequest(requestType: String, args: List<String>): String`)
135
+ acc.line(`}`)
136
+ return acc.finish()
137
+ }
138
+
139
+ private static generateMethodSignature(methodGenerator: Accumulator, name: string, methodDefinition: MethodDefinition, prefix: string = "") {
140
+ methodGenerator.line(`${prefix} suspend fun ${name}(`)
141
+ const argGenerator = methodGenerator.indent()
142
+ for (const argument of getArgs(name, methodDefinition)) {
143
+ const renderedArgument = typeNameKotlin(argument.type)
144
+ argGenerator.line(`${argument.name}: ${renderedArgument.name},`)
145
+ }
146
+ const renderedReturn = typeNameKotlin(methodDefinition.ret)
147
+ methodGenerator.line(`): ${renderedReturn.name}`)
148
+ }
149
+
150
+ generateSendDispatcher(definition: FacadeDefinition): string {
151
+ const acc = new Accumulator()
152
+ KotlinGenerator.generateImports(acc)
153
+ const classBodyAcc = acc.indent()
154
+ acc.line(`class ${definition.name}SendDispatcher (`)
155
+ classBodyAcc.line(`private val json: Json,`)
156
+ classBodyAcc.line(`private val transport : NativeInterface,`)
157
+ acc.line(`) : ${definition.name} {`)
158
+ classBodyAcc.line(`private val encodedFacade = json.encodeToString("${definition.name}")`)
159
+ for (const [methodName, methodDefinition] of Object.entries(definition.methods)) {
160
+ KotlinGenerator.generateMethodSignature(classBodyAcc, methodName, methodDefinition, "override")
161
+ classBodyAcc.line("{")
162
+
163
+ const methodBodyAcc = classBodyAcc.indent()
164
+ methodBodyAcc.line(`val encodedMethod = json.encodeToString("${methodName}")`)
165
+ methodBodyAcc.line("val args : MutableList<String> = mutableListOf()")
166
+ for (let arg of getArgs(methodName, methodDefinition)) {
167
+ methodBodyAcc.line(`args.add(json.encodeToString(${arg.name}))`)
168
+ }
169
+
170
+ if (methodDefinition.ret !== "void") {
171
+ methodBodyAcc.line(`val result = this.transport.sendRequest("ipc", listOf(encodedFacade, encodedMethod) + args)`)
172
+ methodBodyAcc.line(`return json.decodeFromString(result)`)
173
+ } else {
174
+ methodBodyAcc.line(`this.transport.sendRequest("ipc", listOf(encodedFacade, encodedMethod) + args)`)
175
+ }
176
+ classBodyAcc.line(`}`)
177
+ classBodyAcc.line()
178
+ }
179
+
180
+
181
+ acc.line(`}`)
182
+ return acc.finish()
183
+ }
184
+
185
+ generateExtraFiles(): Record<string, string> {
186
+ return {
187
+ "NativeInterface": KotlinGenerator.generateNativeInterface()
188
+ }
189
+ }
190
+
191
+ generateTypeRef(outDir: string, definitionPath: string, definition: TypeRefDefinition): string | null {
192
+ if (definition.location.kotlin) {
193
+ const acc = new Accumulator()
194
+ acc.line(`package de.tutao.tutanota.ipc`)
195
+ acc.line(`typealias ${definition.name} = ${definition.location.kotlin}`)
196
+ return acc.finish()
197
+ } else {
198
+ return null
199
+ }
200
+ }
201
+ }
202
+
203
+ function typeNameKotlin(name: string): RenderedType {
204
+ const parsed = parseType(name)
205
+ return renderKotlinType(parsed)
206
+ }
207
+
208
+ function renderKotlinType(parsed: ParsedType): RenderedType {
209
+ const {baseName, nullable, external} = parsed
210
+ switch (baseName) {
211
+ case "List":
212
+ const renderedListInner = renderKotlinType(parsed.generics[0])
213
+ return {
214
+ externals: renderedListInner.externals,
215
+ name: maybeNullable(`List<${renderedListInner.name}>`, nullable)
216
+ }
217
+ case "Map":
218
+ const renderedKey = renderKotlinType(parsed.generics[0])
219
+ const renderedValue = renderKotlinType(parsed.generics[1])
220
+ return {
221
+ externals: [...renderedKey.externals, ...renderedValue.externals],
222
+ name: maybeNullable(`Map<${renderedKey.name}, ${renderedValue.name}>`, nullable)
223
+ }
224
+ case "string":
225
+ return {externals: [], name: maybeNullable("String", nullable)}
226
+ case "boolean":
227
+ return {externals: [], name: maybeNullable("Boolean", nullable)}
228
+ case "number":
229
+ return {externals: [], name: maybeNullable("Int", nullable)}
230
+ case "bytes":
231
+ return {externals: [], name: maybeNullable("DataWrapper", nullable)}
232
+ case "void":
233
+ return {externals: [], name: maybeNullable("Unit", nullable)}
234
+ default:
235
+ return {externals: [baseName], name: maybeNullable(baseName, nullable)}
236
+ }
237
+ }
238
+
239
+ function maybeNullable(name: string, nullable: boolean) {
240
+ return nullable ? `${name}?` : name
241
+ }
package/lib/Parser.ts ADDED
@@ -0,0 +1,68 @@
1
+ export const PRIMITIVES = ["string", "boolean", "number", "bytes", "void"]
2
+ const KOTLIN_KEYWORDS = [
3
+ "as", "break", "class", "continue", "do", "else", "false", "for", "fun", "if", "in", "interface", "is", "null", "object", "package", "return", "super",
4
+ "this", "throw", "true", "try", "typealias", "typeof", "val", "var", "when", "while"
5
+ ]
6
+ const TYPESCRIPT_KEYWORDS = [
7
+ "var", "const", "let", "break", "return", "case", "catch", "class", "continue", "debugger", "default", "delete", "do", "else", "enum", "export", "extends",
8
+ "false", "finally", "for", "function", "if", "import", "in", "instanceOf", "new", "null", "return", "super", "switch", "this", "throw", "true", "try",
9
+ "typeOf", "void", "while", "with"
10
+ ]
11
+ const SWIFT_KEYWORDS = [
12
+ "Class", "deinit", "Enum", "extension", "Func", "import", "Init", "internal", "Let", "operator", "private", "protocol", "public", "static", "struct", "subscript",
13
+ "typealias", "var", "break", "case", "continue", "default", "do", "else", "fallthrough", "for", "if", "in", "return", "switch", "where", "while",
14
+ "as", "dynamicType", "false", "is", "nil", "self", "Self", "super", "true", "_COLUMN_", "_FILE_", "_FUNCTION_", "_LINE_",
15
+ "associativity", "convenience", "dynamic", "didSet", "final", "get", "infix", "inout", "lazy", "left", "mutating", "none", "nonmutating",
16
+ "optional", "override", "postfix", "precedence", "prefix", "Protocol", "required", "right", "set", "Type", "unowned", "weak", "willSet"
17
+ ]
18
+
19
+ const FORBIDDEN_IDENTIFIERS = new Set([...KOTLIN_KEYWORDS, ...TYPESCRIPT_KEYWORDS, ...SWIFT_KEYWORDS])
20
+
21
+ /**
22
+ * parse a type definition string from the json into a structure that contains all information needed to render
23
+ * language-specific type defs
24
+ */
25
+ export function parseType(typeString: string): ParsedType {
26
+ let nullable = false
27
+ typeString = typeString.trim()
28
+ if (typeString.endsWith("?")) {
29
+ nullable = true
30
+ typeString = typeString.slice(0, -1)
31
+ }
32
+
33
+ const listMatch = typeString.match(/^\s*List<\s*(.*)\s*>\s*$/)
34
+ if (listMatch) {
35
+ const nested = parseType(listMatch[1])
36
+ return {baseName: "List", generics: [nested], nullable, external: false}
37
+ }
38
+
39
+ const mapMatch = typeString.match(/^\s*Map<\s*(.*?),\s*(.*?)\s*>\s*$/)
40
+ if (mapMatch) {
41
+ const nestedKey = parseType(mapMatch[1])
42
+ const nestedValue = parseType(mapMatch[2])
43
+ return {baseName: "Map", generics: [nestedKey, nestedValue], nullable, external: false}
44
+ }
45
+
46
+ // this is a basic type without generic params
47
+ const external = !PRIMITIVES.includes(typeString)
48
+ const willBreakAtLeastOneLang = FORBIDDEN_IDENTIFIERS.has(typeString) && external
49
+ const startsWithLetterOrUnderscore = typeString.match(/^[_a-zA-Z]/)
50
+ if (willBreakAtLeastOneLang || !startsWithLetterOrUnderscore) {
51
+ throw new Error(`illegal identifier: "${typeString}"`)
52
+ }
53
+
54
+ return {baseName: typeString, generics: [], external, nullable}
55
+ } // frontend type
56
+
57
+
58
+ // {basename: List, generics: [{basename: string, generics: []}]>
59
+ export interface ParsedType {
60
+ // this needs an import
61
+ external: boolean
62
+ // this argument or return can be null
63
+ nullable: boolean
64
+ // name as given in the json file, without generics
65
+ baseName: string
66
+ // contained generic types, if any
67
+ generics: ParsedType[]
68
+ }
@@ -0,0 +1,248 @@
1
+ import {FacadeDefinition, getArgs, LangGenerator, MethodDefinition, minusculize, RenderedType, StructDefinition, TypeRefDefinition} from "./common.js"
2
+ import {Accumulator} from "./Accumulator.js"
3
+ import {ParsedType, parseType} from "./Parser.js"
4
+
5
+ export class SwiftGenerator implements LangGenerator {
6
+
7
+ handleStructDefinition(definition: StructDefinition): string {
8
+ const acc = new Accumulator()
9
+ this.generateDocComment(acc, definition.doc)
10
+ acc.line(`public struct ${definition.name} : Codable {`)
11
+ const fieldGenerator = acc.indent()
12
+ for (const [name, fieldDefinition] of Object.entries(definition.fields)) {
13
+ const renderedType = typeNameSwift(fieldDefinition)
14
+ fieldGenerator.line(`let ${name}: ${renderedType.name}`)
15
+ }
16
+ acc.line("}")
17
+ return acc.finish()
18
+ }
19
+
20
+ private generateDocComment(acc: Accumulator, comment: string | null | undefined) {
21
+ if (!comment) return
22
+ acc.line("/**")
23
+ acc.line(` * ${comment}`)
24
+ acc.line(" */")
25
+ }
26
+
27
+ generateFacade(definition: FacadeDefinition): string {
28
+ const acc = new Accumulator()
29
+ acc.line("import Foundation")
30
+ acc.line()
31
+ this.generateDocComment(acc, definition.doc)
32
+ acc.line(`public protocol ${definition.name} {`)
33
+ const methodAcc = acc.indent()
34
+ for (const [name, methodDefinition] of Object.entries(definition.methods)) {
35
+ this.generateDocComment(methodAcc, methodDefinition.doc)
36
+ SwiftGenerator.generateMethodSignature(methodAcc, name, methodDefinition)
37
+ }
38
+ acc.line("}")
39
+ return acc.finish()
40
+ }
41
+
42
+ private static generateMethodSignature(methodGenerator: Accumulator, name: string, methodDefinition: MethodDefinition) {
43
+ methodGenerator.line(`func ${name}(`)
44
+ const argGenerator = methodGenerator.indent()
45
+ const args = getArgs(name, methodDefinition)
46
+ const lastArg = args[args.length - 1]
47
+ for (const argument of args) {
48
+ const renderedArgument = typeNameSwift(argument.type)
49
+ const argLine =
50
+ `_ ${argument.name}: ${renderedArgument.name}` +
51
+ ((argument === lastArg) ? "" : ",")
52
+ argGenerator.line(argLine)
53
+ }
54
+ const renderedReturn = typeNameSwift(methodDefinition.ret)
55
+ methodGenerator.line(`) async throws -> ${renderedReturn.name}`)
56
+ }
57
+
58
+ generateReceiveDispatcher(definition: FacadeDefinition): string {
59
+ const acc = new Accumulator()
60
+ acc.line("import Foundation")
61
+ acc.line()
62
+ acc.line(`public class ${definition.name}ReceiveDispatcher {`)
63
+ const methodAcc = acc.indent()
64
+ methodAcc.line(`let facade: ${definition.name}`)
65
+ methodAcc.line(`init(facade: ${definition.name}) {`)
66
+ methodAcc.indent().line(`self.facade = facade`)
67
+ methodAcc.line(`}`)
68
+
69
+ methodAcc.line(`func dispatch(method: String, arg: [String]) async throws -> String {`)
70
+ const switchAcc = methodAcc.indent()
71
+ switchAcc.line(`switch method {`)
72
+ const caseAcc = switchAcc.indent()
73
+ for (const [methodName, method] of Object.entries(definition.methods)) {
74
+ const arg = getArgs(methodName, method)
75
+ const decodedArgs = []
76
+ for (let i = 0; i < arg.length; i++) {
77
+ const {name: argName, type} = arg[i]
78
+ const renderedArgType = typeNameSwift(type)
79
+ decodedArgs.push([argName, renderedArgType] as const)
80
+ }
81
+ switchAcc.line(`case "${methodName}":`)
82
+ for (let i = 0; i < arg.length; i++) {
83
+ const [argName, argType] = decodedArgs[i]
84
+ caseAcc.line(`let ${argName} = try! JSONDecoder().decode(${argType.name}.self, from: arg[${i}].data(using: .utf8)!)`)
85
+ }
86
+ if (method.ret === "void") {
87
+ caseAcc.line(`try await self.facade.${methodName}(`)
88
+ } else {
89
+ caseAcc.line(`let result = try await self.facade.${methodName}(`)
90
+ }
91
+ for (let i = 0; i < arg.length; i++) {
92
+ const comma = i === arg.length - 1 ? "" : ","
93
+ caseAcc.indent().line(arg[i].name + comma)
94
+ }
95
+ caseAcc.line(")")
96
+ if (method.ret === "void") {
97
+ caseAcc.line(`return "null"`)
98
+ } else {
99
+ caseAcc.line(`return toJson(result)`)
100
+ }
101
+ }
102
+ switchAcc.line(`default:`)
103
+ caseAcc.line(`fatalError("licc messed up! \\(method)")`)
104
+ switchAcc.line(`}`)
105
+ methodAcc.line(`}`)
106
+
107
+ acc.line(`}`)
108
+ return acc.finish()
109
+ }
110
+
111
+ generateGlobalDispatcher(name: string, facadeNames: Array<string>): string {
112
+ const acc = new Accumulator()
113
+ acc.line(`public class ${name} {`)
114
+ const methodAcc = acc.indent()
115
+
116
+ for (let facadeName of facadeNames) {
117
+ methodAcc.line(`private let ${minusculize(facadeName)}: ${facadeName}ReceiveDispatcher`)
118
+ }
119
+ methodAcc.line()
120
+ methodAcc.line(`init(`)
121
+ const lastFacadeName = facadeNames[facadeNames.length - 1]
122
+ for (let facadeName of facadeNames) {
123
+ const comma = facadeName === lastFacadeName ? "" : ","
124
+ methodAcc.indent().line(`${minusculize(facadeName)} : ${facadeName}${comma}`)
125
+ }
126
+ methodAcc.line(`) {`)
127
+ for (let facadeName of facadeNames) {
128
+ methodAcc.indent().line(`self.${minusculize(facadeName)} = ${facadeName}ReceiveDispatcher(facade: ${minusculize(facadeName)})`)
129
+ }
130
+ methodAcc.line(`}`)
131
+ methodAcc.line()
132
+
133
+ methodAcc.line(`func dispatch(facadeName: String, methodName: String, args: Array<String>) async throws -> String {`)
134
+ const switchAcc = methodAcc.indent()
135
+ switchAcc.line(`switch facadeName {`)
136
+ const caseAcc = switchAcc.indent()
137
+ for (let facadeName of facadeNames) {
138
+ caseAcc.line(`case "${facadeName}":`)
139
+ caseAcc.indent().line(`return try await self.${minusculize(facadeName)}.dispatch(method: methodName, arg: args)`)
140
+ }
141
+ caseAcc.line(`default:`)
142
+ caseAcc.indent().line(`fatalError("licc messed up! " + facadeName)`)
143
+ switchAcc.line(`}`)
144
+ methodAcc.line(`}`)
145
+ acc.line(`}`)
146
+ return acc.finish()
147
+ }
148
+
149
+ generateSendDispatcher(definition: FacadeDefinition): string {
150
+ const acc = new Accumulator()
151
+ acc.line("import Foundation")
152
+ acc.line()
153
+ acc.line(`class ${definition.name}SendDispatcher : ${definition.name} {`)
154
+ const classBodyAcc = acc.indent()
155
+ classBodyAcc.line(`private let transport: NativeInterface`)
156
+ classBodyAcc.line(`init(transport: NativeInterface) { self.transport = transport }`)
157
+ classBodyAcc.line()
158
+ for (const [methodName, methodDefinition] of Object.entries(definition.methods)) {
159
+ SwiftGenerator.generateMethodSignature(classBodyAcc, methodName, methodDefinition)
160
+ const methodBodyAcc = classBodyAcc.indent()
161
+ methodBodyAcc.line("{")
162
+ const args = getArgs(methodName, methodDefinition)
163
+ if (args.length > 0) {
164
+ methodBodyAcc.line("var args = [String]()")
165
+ } else {
166
+ // let instead of var so that it shuts up about mutability unused
167
+ methodBodyAcc.line("let args = [String]()")
168
+ }
169
+ for (let arg of args) {
170
+ methodBodyAcc.line(`args.append(toJson(${arg.name}))`)
171
+ }
172
+ methodBodyAcc.line(`let encodedFacadeName = toJson("${definition.name}")`)
173
+ methodBodyAcc.line(`let encodedMethodName = toJson("${methodName}")`)
174
+ if (methodDefinition.ret !== "void") {
175
+ methodBodyAcc.line(`let returnValue = try await self.transport.sendRequest(requestType: "ipc", args: [encodedFacadeName, encodedMethodName] + args)`)
176
+ methodBodyAcc.line(`return try! JSONDecoder().decode(${typeNameSwift(methodDefinition.ret).name}.self, from: returnValue.data(using: .utf8)!)`)
177
+ } else {
178
+ methodBodyAcc.line(`let _ = try await self.transport.sendRequest(requestType: "ipc", args: [encodedFacadeName, encodedMethodName] + args)`)
179
+ }
180
+
181
+ methodBodyAcc.line(`}`)
182
+ classBodyAcc.line()
183
+ }
184
+
185
+ acc.line(`}`)
186
+ return acc.finish()
187
+ }
188
+
189
+ generateExtraFiles(): Record<string, string> {
190
+ return {
191
+ "NativeInterface": SwiftGenerator.generateNativeInterface()
192
+ }
193
+ }
194
+
195
+ private static generateNativeInterface(): string {
196
+ const acc = new Accumulator()
197
+ acc.line(`protocol NativeInterface {`)
198
+ acc.indent().line(`func sendRequest(requestType: String, args: [String]) async throws -> String`)
199
+ acc.line(`}`)
200
+ acc.line()
201
+ acc.line("func toJson<T>(_ thing: T) -> String where T : Encodable {")
202
+ acc.indent().line("return String(data: try! JSONEncoder().encode(thing), encoding: .utf8)!")
203
+ acc.line("}")
204
+ acc.line()
205
+ return acc.finish()
206
+ }
207
+
208
+ generateTypeRef(outDir: string, definitionPath: string, definition: TypeRefDefinition): string | null {
209
+ return null
210
+ }
211
+ }
212
+
213
+ function typeNameSwift(name: string): RenderedType {
214
+ const parsed = parseType(name)
215
+ return renderSwiftType(parsed)
216
+ }
217
+
218
+ function renderSwiftType(parsed: ParsedType): RenderedType {
219
+ const {baseName, nullable} = parsed
220
+ switch (baseName) {
221
+ case "List":
222
+ const renderedListInner = renderSwiftType(parsed.generics[0])
223
+ return {externals: renderedListInner.externals, name: maybeNullable(`[${renderedListInner.name}]`, nullable)}
224
+ case "Map":
225
+ const renderedKey = renderSwiftType(parsed.generics[0])
226
+ const renderedValue = renderSwiftType(parsed.generics[1])
227
+ return {
228
+ externals: [...renderedKey.externals, ...renderedValue.externals],
229
+ name: maybeNullable(`[${renderedKey.name} : ${renderedValue.name}]`, nullable)
230
+ }
231
+ case "string":
232
+ return {externals: [], name: maybeNullable("String", nullable)}
233
+ case "boolean":
234
+ return {externals: [], name: maybeNullable("Bool", nullable)}
235
+ case "number":
236
+ return {externals: [], name: maybeNullable("Int", nullable)}
237
+ case "bytes":
238
+ return {externals: [], name: maybeNullable("DataWrapper", nullable)}
239
+ case "void":
240
+ return {externals: [], name: maybeNullable("Void", nullable)}
241
+ default:
242
+ return {externals: [baseName], name: maybeNullable(baseName, nullable)}
243
+ }
244
+ }
245
+
246
+ function maybeNullable(name: string, nullable: boolean) {
247
+ return nullable ? `${name}?` : name
248
+ }