skir-dart-gen 0.0.1
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.
- package/README.md +409 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +636 -0
- package/dist/index.js.map +1 -0
- package/dist/naming.d.ts +12 -0
- package/dist/naming.d.ts.map +1 -0
- package/dist/naming.js +141 -0
- package/dist/naming.js.map +1 -0
- package/dist/type_speller.d.ts +28 -0
- package/dist/type_speller.d.ts.map +1 -0
- package/dist/type_speller.js +182 -0
- package/dist/type_speller.js.map +1 -0
- package/package.json +50 -0
- package/src/index.ts +880 -0
- package/src/naming.ts +163 -0
- package/src/type_speller.ts +232 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import * as paths from "path";
|
|
2
|
+
import { convertCase, } from "skir-internal";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { enumFieldToDartName, getModuleAlias, structFieldToDartName, toLowerCamel, toTopLevelConstantName, toUpperCamel, } from "./naming.js";
|
|
5
|
+
import { TypeSpeller } from "./type_speller.js";
|
|
6
|
+
const Config = z.object({});
|
|
7
|
+
class DartCodeGenerator {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.id = "dart";
|
|
10
|
+
this.configType = Config;
|
|
11
|
+
this.version = "1.0.0";
|
|
12
|
+
}
|
|
13
|
+
generateCode(input) {
|
|
14
|
+
const { recordMap, config } = input;
|
|
15
|
+
const outputFiles = [];
|
|
16
|
+
for (const module of input.modules) {
|
|
17
|
+
outputFiles.push({
|
|
18
|
+
path: module.path.replace(/\.skir$/, ".dart"),
|
|
19
|
+
code: new DartSourceFileGenerator(module, recordMap, config).generate(),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
return { files: outputFiles };
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// Generates the code for one Dart file.
|
|
26
|
+
class DartSourceFileGenerator {
|
|
27
|
+
constructor(inModule, recordMap, _config) {
|
|
28
|
+
this.inModule = inModule;
|
|
29
|
+
this.code = "";
|
|
30
|
+
this.typeSpeller = new TypeSpeller(recordMap, inModule);
|
|
31
|
+
}
|
|
32
|
+
generate() {
|
|
33
|
+
// http://patorjk.com/software/taag/#f=Doom&t=Do%20not%20edit
|
|
34
|
+
this.push(`// ______ _ _ _ _
|
|
35
|
+
// | _ \\ | | | |(_)| |
|
|
36
|
+
// | | | | ___ _ __ ___ | |_ ___ __| | _ | |_
|
|
37
|
+
// | | | | / _ \\ | '_ \\ / _ \\ | __| / _ \\ / _\` || || __|
|
|
38
|
+
// | |/ / | (_) | | | | || (_) || |_ | __/| (_| || || |_
|
|
39
|
+
// |___/ \\___/ |_| |_| \\___/ \\__| \\___| \\__,_||_| \\__|
|
|
40
|
+
//
|
|
41
|
+
|
|
42
|
+
// To install the Skir client library:
|
|
43
|
+
// dart pub add skir
|
|
44
|
+
|
|
45
|
+
`);
|
|
46
|
+
this.writeImports();
|
|
47
|
+
for (const record of this.inModule.records) {
|
|
48
|
+
const { recordType } = record.record;
|
|
49
|
+
this.pushEol();
|
|
50
|
+
if (recordType === "struct") {
|
|
51
|
+
this.writeClassesForStruct(record);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
this.writeClassesForEnum(record);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const method of this.inModule.methods) {
|
|
58
|
+
this.writeMethod(method);
|
|
59
|
+
}
|
|
60
|
+
for (const constant of this.inModule.constants) {
|
|
61
|
+
this.writeConstant(constant);
|
|
62
|
+
}
|
|
63
|
+
return this.joinLinesAndFixFormatting();
|
|
64
|
+
}
|
|
65
|
+
writeClassesForStruct(struct) {
|
|
66
|
+
const { typeSpeller } = this;
|
|
67
|
+
const { fields } = struct.record;
|
|
68
|
+
const className = typeSpeller.getClassName(struct);
|
|
69
|
+
this.push(`${DartSourceFileGenerator.SEPARATOR}\n`, `// struct ${className.replace("_", ".")}\n`, `${DartSourceFileGenerator.SEPARATOR}\n\n`, `sealed class ${className}_orMutable {\n`);
|
|
70
|
+
for (const field of fields) {
|
|
71
|
+
const dartName = structFieldToDartName(field);
|
|
72
|
+
const allRecordsFrozen = field.isRecursive === "hard";
|
|
73
|
+
const type = field.type;
|
|
74
|
+
const dartType = typeSpeller.getDartType(type, "maybe-mutable", allRecordsFrozen);
|
|
75
|
+
this.push(`${dartType} get ${dartName};\n`);
|
|
76
|
+
}
|
|
77
|
+
if (fields.length) {
|
|
78
|
+
this.pushEol();
|
|
79
|
+
}
|
|
80
|
+
this.push(`${className} toFrozen();\n`, "}\n\n", // class _orMutable
|
|
81
|
+
`final class ${className} implements ${className}_orMutable {\n`);
|
|
82
|
+
for (const field of fields) {
|
|
83
|
+
const dartName = structFieldToDartName(field);
|
|
84
|
+
const type = field.type;
|
|
85
|
+
const dartType = typeSpeller.getDartType(type, "frozen");
|
|
86
|
+
if (field.isRecursive === "hard") {
|
|
87
|
+
this.push(`final ${dartType}? _rec_${dartName};\n`);
|
|
88
|
+
const defaultExpr = this.getDefaultExpression(type).expression;
|
|
89
|
+
this.push(`${dartType} get ${dartName} => _rec_${dartName} ?? ${defaultExpr};\n`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this.push(`final ${dartType} ${dartName};\n`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
this.push(`_skir.internal__UnrecognizedFields? _u;\n\n`);
|
|
96
|
+
// Public constructor
|
|
97
|
+
this.push(`factory ${className}(`);
|
|
98
|
+
this.push(fields.length ? "{\n" : "");
|
|
99
|
+
for (const field of fields) {
|
|
100
|
+
const dartName = structFieldToDartName(field);
|
|
101
|
+
const dartType = typeSpeller.getDartType(field.type, "initializer");
|
|
102
|
+
this.push(`required ${dartType} ${dartName},\n`);
|
|
103
|
+
}
|
|
104
|
+
this.push(fields.length ? "}" : "");
|
|
105
|
+
this.push(`) => ${className}._(\n`);
|
|
106
|
+
for (const field of fields) {
|
|
107
|
+
const dartName = structFieldToDartName(field);
|
|
108
|
+
const toFrozenExpr = this.toFrozenExpression(dartName, field.type);
|
|
109
|
+
this.push(`${toFrozenExpr},\n`);
|
|
110
|
+
}
|
|
111
|
+
this.push(");\n\n");
|
|
112
|
+
// Private constructor
|
|
113
|
+
this.push(`${className}._(\n`);
|
|
114
|
+
for (const field of fields) {
|
|
115
|
+
const dartName = structFieldToDartName(field);
|
|
116
|
+
if (field.isRecursive === "hard") {
|
|
117
|
+
this.push(`this._rec_${dartName},\n`);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.push(`this.${dartName},\n`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
this.push(");\n\n", //
|
|
124
|
+
`static final defaultInstance = ${className}._(\n`);
|
|
125
|
+
for (const field of fields) {
|
|
126
|
+
if (field.isRecursive === "hard") {
|
|
127
|
+
this.push("null,\n");
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
const defaultExpr = this.getDefaultExpression(field.type).expression;
|
|
131
|
+
this.push(`${defaultExpr},\n`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
this.push(");\n\n", `static ${className}_mutable mutable() => ${className}_mutable._(\n`);
|
|
135
|
+
for (const field of fields) {
|
|
136
|
+
const defaultExpr = this.getDefaultExpression(field.type).expression;
|
|
137
|
+
this.push(`${defaultExpr},\n`);
|
|
138
|
+
}
|
|
139
|
+
this.push(`);\n\n`);
|
|
140
|
+
this.push("@_core.deprecated\n", `${className} toFrozen() => this;\n\n`, `${className}_mutable toMutable() => ${className}_mutable._(\n`);
|
|
141
|
+
for (const field of fields) {
|
|
142
|
+
const dartName = structFieldToDartName(field);
|
|
143
|
+
this.push(`this.${dartName},\n`);
|
|
144
|
+
}
|
|
145
|
+
this.push(");\n\n", "_core.bool operator ==(other) {\n", "if (_core.identical(this, other)) return true;\n", `if (other is! ${className}) return false;\n`, "return _skir.internal__listEquality.equals(_equality_proxy, other._equality_proxy);\n", "}\n\n", "_core.int get hashCode => _skir.internal__listEquality.hash(_equality_proxy);\n\n", "_core.List get _equality_proxy => [\n");
|
|
146
|
+
for (const field of fields) {
|
|
147
|
+
const dartName = structFieldToDartName(field);
|
|
148
|
+
this.push(`this.${dartName},\n`);
|
|
149
|
+
}
|
|
150
|
+
this.push("];\n\n", "_core.String toString() => _skir.internal__stringify(this, serializer);\n\n", `static _skir.StructSerializer<${className}, ${className}_mutable> get serializer {\n`, "if (_serializerBuilder.mustInitialize()) {\n");
|
|
151
|
+
for (const field of fields) {
|
|
152
|
+
const dartName = structFieldToDartName(field);
|
|
153
|
+
const serializerExpr = typeSpeller.getSerializerExpression(field.type);
|
|
154
|
+
this.push("_serializerBuilder.addField(\n", `"${field.name.text}",\n`, `"${dartName}",\n`, `${field.number},\n`, `${serializerExpr},\n`, `${toDartStringLiteral(field.doc.text)},\n`, `(it) => it.${dartName},\n`, `(it, v) => it.${dartName} = v,\n`, ");\n");
|
|
155
|
+
}
|
|
156
|
+
for (const removedNumber of struct.record.removedNumbers) {
|
|
157
|
+
this.push(`_serializerBuilder.addRemovedNumber(${removedNumber});\n`);
|
|
158
|
+
}
|
|
159
|
+
this.push("_serializerBuilder.finalize();\n", "}\n", "return _serializerBuilder.serializer;\n", "}\n\n", "static final _serializerBuilder = _skir.internal__StructSerializerBuilder(\n", `recordId: "${getRecordId(struct)}",\n`, `doc: ${toDartStringLiteral(struct.record.doc.text)},\n`, "defaultInstance: defaultInstance,\n", "newMutable: (it) => (it != null) ? it.toMutable() : mutable(),\n", `toFrozen: (${className}_mutable it) => it.toFrozen(),\n`, "getUnrecognizedFields: (it) => it._u,\n", "setUnrecognizedFields: (it, u) => it._u = u,\n", ");\n\n", "}\n\n"); // class frozen
|
|
160
|
+
this.push(`final class ${className}_mutable implements ${className}_orMutable {\n\n`);
|
|
161
|
+
for (const field of fields) {
|
|
162
|
+
const dartName = structFieldToDartName(field);
|
|
163
|
+
const allRecordsFrozen = field.isRecursive === "hard";
|
|
164
|
+
const type = field.type;
|
|
165
|
+
const dartType = typeSpeller.getDartType(type, "maybe-mutable", allRecordsFrozen);
|
|
166
|
+
this.push(`${dartType} ${dartName};\n`);
|
|
167
|
+
}
|
|
168
|
+
this.push(`_skir.internal__UnrecognizedFields? _u;\n\n`, `${className}_mutable._(\n`);
|
|
169
|
+
for (const field of fields) {
|
|
170
|
+
const dartName = structFieldToDartName(field);
|
|
171
|
+
this.push(`this.${dartName},\n`);
|
|
172
|
+
}
|
|
173
|
+
this.push(");\n\n");
|
|
174
|
+
this.writeMutableGetters(fields);
|
|
175
|
+
this.push(`${className} toFrozen() => ${className}(\n`);
|
|
176
|
+
for (const field of fields) {
|
|
177
|
+
const dartName = structFieldToDartName(field);
|
|
178
|
+
this.push(`${dartName}: this.${dartName},\n`);
|
|
179
|
+
}
|
|
180
|
+
this.push(").._u = this._u;\n", "}\n\n");
|
|
181
|
+
}
|
|
182
|
+
writeMutableGetters(fields) {
|
|
183
|
+
const { typeSpeller } = this;
|
|
184
|
+
for (const field of fields) {
|
|
185
|
+
if (field.isRecursive) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
const type = field.type;
|
|
189
|
+
const dartName = structFieldToDartName(field);
|
|
190
|
+
const mutableGetterName = "mutable" + convertCase(field.name.text, "UpperCamel");
|
|
191
|
+
const mutableType = typeSpeller.getDartType(field.type, "mutable");
|
|
192
|
+
const accessor = `this.${dartName}`;
|
|
193
|
+
let bodyLines = [];
|
|
194
|
+
if (type.kind === "array") {
|
|
195
|
+
const typeParameter = mutableType.substring(mutableType.indexOf("<"));
|
|
196
|
+
bodyLines = [
|
|
197
|
+
`if (value is _skir.internal__MutableList${typeParameter}) {\n`,
|
|
198
|
+
" return value;\n",
|
|
199
|
+
"} else {\n",
|
|
200
|
+
` return ${accessor} = _skir.internal__MutableList([...value]);\n`,
|
|
201
|
+
"}\n",
|
|
202
|
+
];
|
|
203
|
+
}
|
|
204
|
+
else if (type.kind === "record") {
|
|
205
|
+
const record = this.typeSpeller.recordMap.get(type.key);
|
|
206
|
+
if (record.record.recordType === "struct") {
|
|
207
|
+
const structQualifiedName = typeSpeller.getClassName(record);
|
|
208
|
+
bodyLines = [
|
|
209
|
+
`if (value is ${structQualifiedName}_mutable) {\n`,
|
|
210
|
+
" return value;\n",
|
|
211
|
+
"} else {\n",
|
|
212
|
+
` return ${accessor} = (value as ${structQualifiedName}).toMutable();\n`,
|
|
213
|
+
"}\n",
|
|
214
|
+
];
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (bodyLines.length) {
|
|
218
|
+
this.push(`${mutableType} get ${mutableGetterName} {\n`, `final value = ${accessor};\n`);
|
|
219
|
+
for (const line of bodyLines) {
|
|
220
|
+
this.push(line);
|
|
221
|
+
}
|
|
222
|
+
this.push("}\n\n");
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
writeClassesForEnum(record) {
|
|
227
|
+
const { typeSpeller } = this;
|
|
228
|
+
const { fields } = record.record;
|
|
229
|
+
const constantFields = fields.filter((f) => !f.type);
|
|
230
|
+
const wrapperFields = fields.filter((f) => f.type);
|
|
231
|
+
const className = typeSpeller.getClassName(record);
|
|
232
|
+
// The actual enum class
|
|
233
|
+
this.push(`${DartSourceFileGenerator.SEPARATOR}\n`, `// enum ${className.replace("_", ".")}\n`, `${DartSourceFileGenerator.SEPARATOR}\n\n`, `sealed class ${className} {\n`, `static const ${className} unknown = ${className}_unknown._instance;\n\n`);
|
|
234
|
+
for (const field of constantFields) {
|
|
235
|
+
this.push(`static const ${enumFieldToDartName(field)} = `, `_${className}_consts.${toLowerCamel(field)}Const;\n`);
|
|
236
|
+
}
|
|
237
|
+
this.pushEol();
|
|
238
|
+
for (const field of wrapperFields) {
|
|
239
|
+
const type = field.type;
|
|
240
|
+
const dartType = typeSpeller.getDartType(type, "frozen");
|
|
241
|
+
this.push(`factory ${className}.wrap${toUpperCamel(field)}(\n`, `${dartType} value\n`, `) => ${className}_${toLowerCamel(field)}Wrapper._(value);\n\n`);
|
|
242
|
+
if (type.kind === "record") {
|
|
243
|
+
const record = typeSpeller.recordMap.get(type.key);
|
|
244
|
+
if (record.record.recordType === "struct") {
|
|
245
|
+
const struct = record.record;
|
|
246
|
+
this.push(`factory ${className}.create${toUpperCamel(field)}(`);
|
|
247
|
+
const structFields = struct.fields;
|
|
248
|
+
this.push(structFields.length ? "{\n" : "");
|
|
249
|
+
for (const structField of structFields) {
|
|
250
|
+
const dartName = structFieldToDartName(structField);
|
|
251
|
+
const structType = structField.type;
|
|
252
|
+
const dartType = typeSpeller.getDartType(structType, "initializer");
|
|
253
|
+
this.push(`required ${dartType} ${dartName},\n`);
|
|
254
|
+
}
|
|
255
|
+
this.push(structFields.length ? "}" : "");
|
|
256
|
+
this.push(`) => ${className}.wrap${toUpperCamel(field)}(\n`);
|
|
257
|
+
this.push(`${typeSpeller.getClassName(record)}(\n`);
|
|
258
|
+
for (const structField of structFields) {
|
|
259
|
+
const dartName = structFieldToDartName(structField);
|
|
260
|
+
this.push(`${dartName}: ${dartName},\n`);
|
|
261
|
+
}
|
|
262
|
+
this.push(")\n", ");\n\n");
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
this.push(`\n${className}_kind get kind;\n`);
|
|
267
|
+
this.push(`static _skir.EnumSerializer<${className}> get serializer {\n`, "if (_serializerBuilder.mustInitialize()) {\n");
|
|
268
|
+
for (const constantField of constantFields) {
|
|
269
|
+
const dartName = enumFieldToDartName(constantField);
|
|
270
|
+
this.push("_serializerBuilder.addConstantVariant(\n", `${constantField.number},\n`, `"${constantField.name.text}",\n`, `"${dartName}",\n`, `${toDartStringLiteral(constantField.doc.text)},\n`, `${dartName},\n`, ");\n");
|
|
271
|
+
}
|
|
272
|
+
for (const wrapperField of wrapperFields) {
|
|
273
|
+
const type = wrapperField.type;
|
|
274
|
+
const serializerExpr = typeSpeller.getSerializerExpression(type);
|
|
275
|
+
this.push("_serializerBuilder.addWrapperVariant(\n", `${wrapperField.number},\n`, `"${wrapperField.name.text}",\n`, `"wrap${toUpperCamel(wrapperField)}",\n`, `${serializerExpr},\n`, `${toDartStringLiteral(wrapperField.doc.text)},\n`, `${className}_${toLowerCamel(wrapperField)}Wrapper._,\n`, "(it) => it.value,\n", `ordinal: ${className}_kind.${toLowerCamel(wrapperField)}Wrapper._ordinal,\n`, ");\n");
|
|
276
|
+
}
|
|
277
|
+
for (const removedNumber of record.record.removedNumbers) {
|
|
278
|
+
this.push(`_serializerBuilder.addRemovedNumber(${removedNumber});\n`);
|
|
279
|
+
}
|
|
280
|
+
this.push("_serializerBuilder.finalize();\n", "}\n", "return _serializerBuilder.serializer;\n", "}\n\n", "static final _serializerBuilder = _skir.internal__EnumSerializerBuilder.create(\n", `recordId: "${getRecordId(record)}",\n`, `doc: ${toDartStringLiteral(record.record.doc.text)},\n`, `unknownInstance: ${className}_unknown._instance,\n`, `enumInstance: ${className}.unknown,\n`, `getOrdinal: (it) => it.kind._ordinal,\n`, `wrapUnrecognized: ${className}_unknown._unrecognized,\n`, `getUnrecognized: (it) => it._u,\n`, ");\n", "}\n\n");
|
|
281
|
+
// The _kind enum
|
|
282
|
+
this.push(`enum ${className}_kind {\n`, //
|
|
283
|
+
"unknown(0),\n");
|
|
284
|
+
let ordinalCounter = 1;
|
|
285
|
+
for (const field of constantFields) {
|
|
286
|
+
const ordinal = ordinalCounter++;
|
|
287
|
+
this.push(`${toLowerCamel(field)}Const(${ordinal}),\n`);
|
|
288
|
+
}
|
|
289
|
+
for (const field of wrapperFields) {
|
|
290
|
+
const ordinal = ordinalCounter++;
|
|
291
|
+
this.push(`${toLowerCamel(field)}Wrapper(${ordinal}),\n`);
|
|
292
|
+
}
|
|
293
|
+
this.replaceEnd(",\n", ";\n\n");
|
|
294
|
+
this.push("final _core.int _ordinal;\n\n", `const ${className}_kind(this._ordinal);\n\n`, "}\n\n");
|
|
295
|
+
// The _unknown class
|
|
296
|
+
this.push(`final class ${className}_unknown implements ${className} {\n`, `static const _instance = ${className}_unknown._();\n\n`, "final _skir.internal__UnrecognizedVariant? _u;\n\n", `const ${className}_unknown._() : _u = null;\n`, `${className}_unknown._unrecognized(this._u);\n\n`, `${className}_kind get kind => ${className}_kind.unknown;\n`, `_core.bool operator ==(other) => other is ${className}_unknown;\n`, "_core.int get hashCode => 8118964;\n", "_core.String toString() => _skir.internal__stringify(this, ", `${className}.serializer);\n`, "}\n\n");
|
|
297
|
+
// The _consts_ internal enum
|
|
298
|
+
if (constantFields.length) {
|
|
299
|
+
this.push(`enum _${className}_consts implements ${className} {\n`);
|
|
300
|
+
for (const field of constantFields) {
|
|
301
|
+
const name = toLowerCamel(field) + "Const";
|
|
302
|
+
this.push(`${name}(${className}_kind.${name}),\n`);
|
|
303
|
+
}
|
|
304
|
+
this.replaceEnd(",\n", ";\n\n");
|
|
305
|
+
this.push(`final ${className}_kind kind;\n\n`, `const _${className}_consts(this.kind);\n\n`, "_core.String toString() => _skir.internal__stringify(this, ", `${className}.serializer);\n`);
|
|
306
|
+
this.push("}\n\n"); // enum _consts
|
|
307
|
+
}
|
|
308
|
+
if (wrapperFields.length) {
|
|
309
|
+
// The _wrapper abstract class
|
|
310
|
+
this.push(`sealed class _${className}_wrapper implements ${className} {\n`, "_core.dynamic get value;\n\n", "_core.bool operator ==(other) {\n", `if (other is! _${className}_wrapper) return false;\n`, "return kind == other.kind && value == other.value;\n", "}\n\n", "_core.int get hashCode => (kind._ordinal * 31) ^ value.hashCode;\n\n", "_core.String toString() => _skir.internal__stringify(this, ", `${className}.serializer);\n`, "}\n\n");
|
|
311
|
+
for (const field of wrapperFields) {
|
|
312
|
+
const dartType = typeSpeller.getDartType(field.type, "frozen");
|
|
313
|
+
this.push(`final class ${className}_${toLowerCamel(field)}Wrapper `, `extends _${className}_wrapper {\n`, `final ${dartType} value;\n\n`, `${className}_${toLowerCamel(field)}Wrapper._(this.value);\n\n`, `${className}_kind get kind => ${className}_kind.${toLowerCamel(field)}Wrapper;\n`, "}\n\n");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
writeMethod(method) {
|
|
318
|
+
const { typeSpeller } = this;
|
|
319
|
+
const methodName = method.name.text;
|
|
320
|
+
const skirName = convertCase(methodName, "lowerCamel") + "Method";
|
|
321
|
+
const requestType = typeSpeller.getDartType(method.requestType, "frozen");
|
|
322
|
+
const requestSerializerExpr = typeSpeller.getSerializerExpression(method.requestType);
|
|
323
|
+
const responseType = typeSpeller.getDartType(method.responseType, "frozen");
|
|
324
|
+
const responseSerializerExpr = typeSpeller.getSerializerExpression(method.responseType);
|
|
325
|
+
this.push(`final _skir.Method<\n${requestType},\n${responseType}\n> ${skirName} = \n`, "_skir.Method(\n", `"${methodName}",\n`, `${method.number},\n`, requestSerializerExpr + ",\n", responseSerializerExpr + ",\n", `${toDartStringLiteral(method.doc.text)},\n`, ");\n\n");
|
|
326
|
+
}
|
|
327
|
+
writeConstant(constant) {
|
|
328
|
+
const { typeSpeller } = this;
|
|
329
|
+
const type = constant.type;
|
|
330
|
+
const name = toTopLevelConstantName(constant);
|
|
331
|
+
const dartType = typeSpeller.getDartType(type, "frozen");
|
|
332
|
+
const tryGetDartConstLiteral = () => {
|
|
333
|
+
if (type.kind !== "primitive") {
|
|
334
|
+
return undefined;
|
|
335
|
+
}
|
|
336
|
+
const { valueAsDenseJson } = constant;
|
|
337
|
+
switch (type.primitive) {
|
|
338
|
+
case "bool":
|
|
339
|
+
return JSON.stringify(!!valueAsDenseJson);
|
|
340
|
+
case "int32":
|
|
341
|
+
case "string":
|
|
342
|
+
return toDartStringLiteral(valueAsDenseJson);
|
|
343
|
+
case "int64":
|
|
344
|
+
return valueAsDenseJson.toString();
|
|
345
|
+
case "float32":
|
|
346
|
+
case "float64": {
|
|
347
|
+
if (valueAsDenseJson === "NaN") {
|
|
348
|
+
return "_core.double.nan";
|
|
349
|
+
}
|
|
350
|
+
else if (valueAsDenseJson === "Infinity") {
|
|
351
|
+
return "_core.double.infinity";
|
|
352
|
+
}
|
|
353
|
+
else if (valueAsDenseJson === "-Infinity") {
|
|
354
|
+
return "-_core.double.infinity";
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
return JSON.stringify(valueAsDenseJson);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
case "uint64":
|
|
361
|
+
case "bytes":
|
|
362
|
+
case "timestamp":
|
|
363
|
+
return undefined;
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
const dartConstLiteral = tryGetDartConstLiteral();
|
|
367
|
+
if (dartConstLiteral !== undefined) {
|
|
368
|
+
this.push(`const ${dartType} ${name} = ${dartConstLiteral};\n\n`);
|
|
369
|
+
}
|
|
370
|
+
else {
|
|
371
|
+
const serializerExpression = typeSpeller.getSerializerExpression(constant.type);
|
|
372
|
+
const jsonStringLiteral = toDartStringLiteral(JSON.stringify(constant.valueAsDenseJson));
|
|
373
|
+
this.push(`final ${dartType} ${name} = (\n`, serializerExpression, `.fromJsonCode(${jsonStringLiteral})\n`, ");\n\n");
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
getDefaultExpression(type) {
|
|
377
|
+
switch (type.kind) {
|
|
378
|
+
case "primitive": {
|
|
379
|
+
switch (type.primitive) {
|
|
380
|
+
case "bool":
|
|
381
|
+
return { expression: "false", isConst: true };
|
|
382
|
+
case "int32":
|
|
383
|
+
case "int64":
|
|
384
|
+
return { expression: "0", isConst: true };
|
|
385
|
+
case "uint64":
|
|
386
|
+
return { expression: "_core.BigInt.zero", isConst: false };
|
|
387
|
+
case "float32":
|
|
388
|
+
case "float64":
|
|
389
|
+
return { expression: "0.0", isConst: true };
|
|
390
|
+
case "timestamp":
|
|
391
|
+
return { expression: "_skir.unixEpoch", isConst: false };
|
|
392
|
+
case "string":
|
|
393
|
+
return { expression: '""', isConst: true };
|
|
394
|
+
case "bytes":
|
|
395
|
+
return { expression: "_skir.ByteString.empty", isConst: false };
|
|
396
|
+
}
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
case "array": {
|
|
400
|
+
return { expression: `_skir.KeyedIterable.empty`, isConst: true };
|
|
401
|
+
}
|
|
402
|
+
case "optional": {
|
|
403
|
+
return { expression: "null", isConst: true };
|
|
404
|
+
}
|
|
405
|
+
case "record": {
|
|
406
|
+
const record = this.typeSpeller.recordMap.get(type.key);
|
|
407
|
+
const kotlinType = this.typeSpeller.getDartType(type, "frozen");
|
|
408
|
+
switch (record.record.recordType) {
|
|
409
|
+
case "struct": {
|
|
410
|
+
return {
|
|
411
|
+
expression: `${kotlinType}.defaultInstance`,
|
|
412
|
+
isConst: false,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
case "enum": {
|
|
416
|
+
return { expression: `${kotlinType}.unknown`, isConst: true };
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
break;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
toFrozenExpression(inputExpr, type) {
|
|
424
|
+
switch (type.kind) {
|
|
425
|
+
case "primitive": {
|
|
426
|
+
switch (type.primitive) {
|
|
427
|
+
case "timestamp":
|
|
428
|
+
return `${inputExpr}.toUtc()`;
|
|
429
|
+
default:
|
|
430
|
+
return inputExpr;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
case "array": {
|
|
434
|
+
const itemToFrozenExpr = this.toFrozenExpression("it", type.item);
|
|
435
|
+
if (type.key) {
|
|
436
|
+
const path = type.key.path
|
|
437
|
+
.map((f) => structFieldToDartName(f.name.text))
|
|
438
|
+
.join(".");
|
|
439
|
+
if (itemToFrozenExpr === "it") {
|
|
440
|
+
return `_skir.internal__keyedCopy(${inputExpr}, "${path}", (it) => it.${path})`;
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
return `_skir.internal__keyedMappedCopy(${inputExpr}, "${path}", (it) => it.${path}, (it) => ${itemToFrozenExpr})`;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
else {
|
|
447
|
+
if (itemToFrozenExpr === "it") {
|
|
448
|
+
return `_skir.internal__frozenCopy(${inputExpr})`;
|
|
449
|
+
}
|
|
450
|
+
else {
|
|
451
|
+
return `_skir.internal__frozenMappedCopy(${inputExpr}, (it) => ${itemToFrozenExpr})`;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
case "optional": {
|
|
456
|
+
const otherExpr = this.toFrozenExpression(inputExpr, type.other);
|
|
457
|
+
if (otherExpr === inputExpr) {
|
|
458
|
+
return otherExpr;
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
return `(${inputExpr} != null) ? ${otherExpr} : null`;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
case "record": {
|
|
465
|
+
const record = this.typeSpeller.recordMap.get(type.key);
|
|
466
|
+
if (record.record.recordType === "struct") {
|
|
467
|
+
return `${inputExpr}.toFrozen()`;
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
return inputExpr;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
writeImports() {
|
|
476
|
+
this.push('import "dart:core" as _core;\n');
|
|
477
|
+
this.push('import "package:skir/skir.dart" as _skir;\n');
|
|
478
|
+
if (this.inModule.pathToImportedNames.length) {
|
|
479
|
+
this.pushEol();
|
|
480
|
+
}
|
|
481
|
+
const thisPath = paths.dirname(this.inModule.path);
|
|
482
|
+
for (const path of Object.keys(this.inModule.pathToImportedNames)) {
|
|
483
|
+
let dartPath = paths.relative(thisPath, path).replace(/\.skir/, ".dart");
|
|
484
|
+
if (!dartPath.startsWith(".")) {
|
|
485
|
+
dartPath = `./${dartPath}`;
|
|
486
|
+
}
|
|
487
|
+
const alias = getModuleAlias(path);
|
|
488
|
+
this.push(`import "${dartPath}" as ${alias};\n`);
|
|
489
|
+
}
|
|
490
|
+
this.pushEol();
|
|
491
|
+
}
|
|
492
|
+
replaceEnd(searchString, replaceString) {
|
|
493
|
+
if (!this.code.endsWith(searchString)) {
|
|
494
|
+
throw new Error();
|
|
495
|
+
}
|
|
496
|
+
this.code = this.code.slice(0, -searchString.length) + replaceString;
|
|
497
|
+
}
|
|
498
|
+
push(...code) {
|
|
499
|
+
this.code += code.join("");
|
|
500
|
+
}
|
|
501
|
+
pushEol() {
|
|
502
|
+
this.code += "\n";
|
|
503
|
+
}
|
|
504
|
+
joinLinesAndFixFormatting() {
|
|
505
|
+
const indentUnit = " ";
|
|
506
|
+
let result = "";
|
|
507
|
+
// The indent at every line is obtained by repeating indentUnit N times,
|
|
508
|
+
// where N is the length of this array.
|
|
509
|
+
const contextStack = [];
|
|
510
|
+
// Returns the last element in `contextStack`.
|
|
511
|
+
const peakTop = () => contextStack.at(-1);
|
|
512
|
+
const getMatchingLeftBracket = (r) => {
|
|
513
|
+
switch (r) {
|
|
514
|
+
case "}":
|
|
515
|
+
return "{";
|
|
516
|
+
case ")":
|
|
517
|
+
return "(";
|
|
518
|
+
case "]":
|
|
519
|
+
return "[";
|
|
520
|
+
case ">":
|
|
521
|
+
return "<";
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
for (let line of this.code.split("\n")) {
|
|
525
|
+
line = line.trim();
|
|
526
|
+
if (line.length <= 0) {
|
|
527
|
+
// Don't indent empty lines.
|
|
528
|
+
result += "\n";
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
const firstChar = line[0];
|
|
532
|
+
switch (firstChar) {
|
|
533
|
+
case "}":
|
|
534
|
+
case ")":
|
|
535
|
+
case "]":
|
|
536
|
+
case ">": {
|
|
537
|
+
const left = getMatchingLeftBracket(firstChar);
|
|
538
|
+
while (contextStack.pop() !== left) {
|
|
539
|
+
if (contextStack.length <= 0) {
|
|
540
|
+
throw Error();
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
break;
|
|
544
|
+
}
|
|
545
|
+
case ".": {
|
|
546
|
+
if (peakTop() !== ".") {
|
|
547
|
+
contextStack.push(".");
|
|
548
|
+
}
|
|
549
|
+
break;
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
const indent = indentUnit.repeat(contextStack.length);
|
|
553
|
+
result += `${indent}${line.trimEnd()}\n`;
|
|
554
|
+
if (line.startsWith("//")) {
|
|
555
|
+
continue;
|
|
556
|
+
}
|
|
557
|
+
const lastChar = line.slice(-1);
|
|
558
|
+
switch (lastChar) {
|
|
559
|
+
case "{":
|
|
560
|
+
case "(":
|
|
561
|
+
case "[":
|
|
562
|
+
case "<": {
|
|
563
|
+
// The next line will be indented
|
|
564
|
+
contextStack.push(lastChar);
|
|
565
|
+
break;
|
|
566
|
+
}
|
|
567
|
+
case ":":
|
|
568
|
+
case "=": {
|
|
569
|
+
if (peakTop() !== ":") {
|
|
570
|
+
contextStack.push(":");
|
|
571
|
+
}
|
|
572
|
+
break;
|
|
573
|
+
}
|
|
574
|
+
case ";":
|
|
575
|
+
case ",": {
|
|
576
|
+
if (peakTop() === "." || peakTop() === ":") {
|
|
577
|
+
contextStack.pop();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return (result
|
|
583
|
+
// Remove spaces enclosed within curly brackets if that's all there is.
|
|
584
|
+
.replace(/\{\s+\}/g, "{}")
|
|
585
|
+
// Remove spaces enclosed within round brackets if that's all there is.
|
|
586
|
+
.replace(/\(\s+\)/g, "()")
|
|
587
|
+
// Remove spaces enclosed within square brackets if that's all there is.
|
|
588
|
+
.replace(/\[\s+\]/g, "[]")
|
|
589
|
+
// Remove empty line following an open curly bracket.
|
|
590
|
+
.replace(/(\{\n *)\n/g, "$1")
|
|
591
|
+
// Remove empty line preceding a closed curly bracket.
|
|
592
|
+
.replace(/\n(\n *\})/g, "$1")
|
|
593
|
+
// Coalesce consecutive empty lines.
|
|
594
|
+
.replace(/\n\n\n+/g, "\n\n")
|
|
595
|
+
.replace(/\n\n$/g, "\n"));
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
DartSourceFileGenerator.SEPARATOR = `// ${"-".repeat(80 - "// ".length)}`;
|
|
599
|
+
function getRecordId(record) {
|
|
600
|
+
const modulePath = record.modulePath;
|
|
601
|
+
const qualifiedRecordName = record.recordAncestors
|
|
602
|
+
.map((r) => r.name.text)
|
|
603
|
+
.join(".");
|
|
604
|
+
return `${modulePath}:${qualifiedRecordName}`;
|
|
605
|
+
}
|
|
606
|
+
function toDartStringLiteral(input) {
|
|
607
|
+
const escaped = input.replace(/[\\"\n\r\t\b\f\v$\x00-\x1F\x7F]/g, (char) => {
|
|
608
|
+
// Handle common escape sequences
|
|
609
|
+
switch (char) {
|
|
610
|
+
case "\\":
|
|
611
|
+
return "\\\\";
|
|
612
|
+
case '"':
|
|
613
|
+
return '\\"';
|
|
614
|
+
case "\n":
|
|
615
|
+
return "\\n";
|
|
616
|
+
case "\r":
|
|
617
|
+
return "\\r";
|
|
618
|
+
case "\t":
|
|
619
|
+
return "\\t";
|
|
620
|
+
case "\b":
|
|
621
|
+
return "\\b";
|
|
622
|
+
case "\f":
|
|
623
|
+
return "\\f";
|
|
624
|
+
case "\v":
|
|
625
|
+
return "\\v";
|
|
626
|
+
case "$":
|
|
627
|
+
return "\\$";
|
|
628
|
+
default:
|
|
629
|
+
// For other control characters, use Unicode escaping
|
|
630
|
+
return `\\u{${char.charCodeAt(0).toString(16).padStart(4, "0")}}`;
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
return `"${escaped}"`;
|
|
634
|
+
}
|
|
635
|
+
export const GENERATOR = new DartCodeGenerator();
|
|
636
|
+
//# sourceMappingURL=index.js.map
|