@typespec/http-server-csharp 0.58.0-alpha.1 → 0.58.0-alpha.10-dev.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/cmd/hscs.js +2 -0
- package/dist/src/cli/cli.d.ts +2 -0
- package/dist/src/cli/cli.d.ts.map +1 -0
- package/dist/src/cli/cli.js +124 -0
- package/dist/src/cli/cli.js.map +1 -0
- package/dist/src/{attributes.d.ts → lib/attributes.d.ts} +1 -1
- package/dist/src/lib/attributes.d.ts.map +1 -0
- package/dist/src/{attributes.js → lib/attributes.js} +52 -8
- package/dist/src/lib/attributes.js.map +1 -0
- package/dist/src/{boilerplate.d.ts → lib/boilerplate.d.ts} +2 -0
- package/dist/src/lib/boilerplate.d.ts.map +1 -0
- package/dist/src/{boilerplate.js → lib/boilerplate.js} +105 -40
- package/dist/src/lib/boilerplate.js.map +1 -0
- package/dist/src/lib/index.d.ts.map +1 -0
- package/dist/src/lib/index.js.map +1 -0
- package/dist/src/{interfaces.d.ts → lib/interfaces.d.ts} +23 -0
- package/dist/src/lib/interfaces.d.ts.map +1 -0
- package/dist/src/{interfaces.js → lib/interfaces.js} +7 -3
- package/dist/src/lib/interfaces.js.map +1 -0
- package/dist/src/{lib.d.ts → lib/lib.d.ts} +25 -1
- package/dist/src/lib/lib.d.ts.map +1 -0
- package/dist/src/{lib.js → lib/lib.js} +31 -0
- package/dist/src/lib/lib.js.map +1 -0
- package/dist/src/lib/scaffolding.d.ts +21 -0
- package/dist/src/lib/scaffolding.d.ts.map +1 -0
- package/dist/src/lib/scaffolding.js +412 -0
- package/dist/src/lib/scaffolding.js.map +1 -0
- package/dist/src/lib/service.d.ts.map +1 -0
- package/dist/src/{service.js → lib/service.js} +332 -230
- package/dist/src/lib/service.js.map +1 -0
- package/dist/src/lib/testing/index.d.ts.map +1 -0
- package/dist/src/{testing → lib/testing}/index.js +1 -0
- package/dist/src/lib/testing/index.js.map +1 -0
- package/dist/src/lib/type-helpers.d.ts.map +1 -0
- package/dist/src/lib/type-helpers.js.map +1 -0
- package/dist/src/lib/utils.d.ts +88 -0
- package/dist/src/lib/utils.d.ts.map +1 -0
- package/dist/src/lib/utils.js +1173 -0
- package/dist/src/lib/utils.js.map +1 -0
- package/package.json +32 -23
- package/dist/src/attributes.d.ts.map +0 -1
- package/dist/src/attributes.js.map +0 -1
- package/dist/src/boilerplate.d.ts.map +0 -1
- package/dist/src/boilerplate.js.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/interfaces.d.ts.map +0 -1
- package/dist/src/interfaces.js.map +0 -1
- package/dist/src/lib.d.ts.map +0 -1
- package/dist/src/lib.js.map +0 -1
- package/dist/src/service.d.ts.map +0 -1
- package/dist/src/service.js.map +0 -1
- package/dist/src/testing/index.d.ts.map +0 -1
- package/dist/src/testing/index.js.map +0 -1
- package/dist/src/type-helpers.d.ts.map +0 -1
- package/dist/src/type-helpers.js.map +0 -1
- package/dist/src/utils.d.ts +0 -48
- package/dist/src/utils.d.ts.map +0 -1
- package/dist/src/utils.js +0 -628
- package/dist/src/utils.js.map +0 -1
- /package/dist/src/{index.d.ts → lib/index.d.ts} +0 -0
- /package/dist/src/{index.js → lib/index.js} +0 -0
- /package/dist/src/{service.d.ts → lib/service.d.ts} +0 -0
- /package/dist/src/{testing → lib/testing}/index.d.ts +0 -0
- /package/dist/src/{type-helpers.d.ts → lib/type-helpers.d.ts} +0 -0
- /package/dist/src/{type-helpers.js → lib/type-helpers.js} +0 -0
|
@@ -0,0 +1,1173 @@
|
|
|
1
|
+
import { NoTarget, getFriendlyName, isNullType, isNumericType, isTemplateInstance, isUnknownType, isVoidType, } from "@typespec/compiler";
|
|
2
|
+
import { StringBuilder, code, } from "@typespec/compiler/emitter-framework";
|
|
3
|
+
import { Visibility, createMetadataInfo, getHeaderFieldName, isBody, isBodyRoot, isHeader, isMetadata, isPathParam, isQueryParam, isStatusCode, } from "@typespec/http";
|
|
4
|
+
import { camelCase, pascalCase } from "change-case";
|
|
5
|
+
import { getAttributes } from "./attributes.js";
|
|
6
|
+
import { BooleanValue, CSharpType, NameCasingType, NullValue, NumericValue, StringValue, } from "./interfaces.js";
|
|
7
|
+
import { reportDiagnostic } from "./lib.js";
|
|
8
|
+
const _scalars = new Map();
|
|
9
|
+
export function getCSharpTypeForScalar(program, scalar) {
|
|
10
|
+
if (_scalars.has(scalar))
|
|
11
|
+
return _scalars.get(scalar);
|
|
12
|
+
if (program.checker.isStdType(scalar)) {
|
|
13
|
+
return getCSharpTypeForStdScalars(program, scalar);
|
|
14
|
+
}
|
|
15
|
+
if (scalar.baseScalar) {
|
|
16
|
+
return getCSharpTypeForScalar(program, scalar.baseScalar);
|
|
17
|
+
}
|
|
18
|
+
reportDiagnostic(program, {
|
|
19
|
+
code: "unrecognized-scalar",
|
|
20
|
+
format: { typeName: scalar.name },
|
|
21
|
+
target: scalar,
|
|
22
|
+
});
|
|
23
|
+
const result = new CSharpType({
|
|
24
|
+
name: "object",
|
|
25
|
+
namespace: "System",
|
|
26
|
+
isBuiltIn: true,
|
|
27
|
+
isValueType: false,
|
|
28
|
+
});
|
|
29
|
+
_scalars.set(scalar, result);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
export const UnknownType = new CSharpType({
|
|
33
|
+
name: "JsonNode",
|
|
34
|
+
namespace: "System.Text.Json",
|
|
35
|
+
isValueType: false,
|
|
36
|
+
isBuiltIn: true,
|
|
37
|
+
});
|
|
38
|
+
export function getCSharpType(program, type, namespace) {
|
|
39
|
+
const known = getKnownType(program, type);
|
|
40
|
+
if (known !== undefined)
|
|
41
|
+
return { type: known };
|
|
42
|
+
switch (type.kind) {
|
|
43
|
+
case "Boolean":
|
|
44
|
+
return { type: standardScalars.get("boolean"), value: new BooleanValue(type.value) };
|
|
45
|
+
case "Number":
|
|
46
|
+
return { type: standardScalars.get("numeric"), value: new NumericValue(type.value) };
|
|
47
|
+
case "String":
|
|
48
|
+
return { type: standardScalars.get("string"), value: new StringValue(type.value) };
|
|
49
|
+
case "EnumMember":
|
|
50
|
+
const enumValue = type.value === undefined ? type.name : type.value;
|
|
51
|
+
if (typeof enumValue === "string")
|
|
52
|
+
return { type: standardScalars.get("string"), value: new StringValue(enumValue) };
|
|
53
|
+
else
|
|
54
|
+
return { type: standardScalars.get("numeric"), value: new NumericValue(enumValue) };
|
|
55
|
+
case "Intrinsic":
|
|
56
|
+
return getCSharpTypeForIntrinsic(program, type);
|
|
57
|
+
case "Object":
|
|
58
|
+
return { type: UnknownType };
|
|
59
|
+
case "ModelProperty":
|
|
60
|
+
return getCSharpType(program, type.type, namespace);
|
|
61
|
+
case "Scalar":
|
|
62
|
+
return { type: getCSharpTypeForScalar(program, type) };
|
|
63
|
+
case "Tuple":
|
|
64
|
+
const resolvedItem = coalesceTypes(program, type.values, namespace);
|
|
65
|
+
const itemType = resolvedItem.type;
|
|
66
|
+
return {
|
|
67
|
+
type: new CSharpType({
|
|
68
|
+
name: `${itemType.name}[]`,
|
|
69
|
+
namespace: itemType.namespace,
|
|
70
|
+
isBuiltIn: itemType.isBuiltIn,
|
|
71
|
+
isValueType: false,
|
|
72
|
+
}),
|
|
73
|
+
};
|
|
74
|
+
case "UnionVariant":
|
|
75
|
+
return getCSharpType(program, type.type, namespace);
|
|
76
|
+
case "Union":
|
|
77
|
+
return coalesceTypes(program, [...type.variants.values()].map((v) => v.type), namespace);
|
|
78
|
+
case "Interface":
|
|
79
|
+
return {
|
|
80
|
+
type: new CSharpType({
|
|
81
|
+
name: ensureCSharpIdentifier(program, type, type.name, NameCasingType.Class),
|
|
82
|
+
namespace: namespace || "Models",
|
|
83
|
+
isBuiltIn: false,
|
|
84
|
+
isValueType: false,
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
case "Enum":
|
|
88
|
+
return {
|
|
89
|
+
type: new CSharpType({
|
|
90
|
+
name: ensureCSharpIdentifier(program, type, type.name, NameCasingType.Class),
|
|
91
|
+
namespace: `${namespace}.Models`,
|
|
92
|
+
isBuiltIn: false,
|
|
93
|
+
isValueType: false,
|
|
94
|
+
}),
|
|
95
|
+
};
|
|
96
|
+
case "Model":
|
|
97
|
+
if (type.indexer !== undefined && isNumericType(program, type.indexer?.key)) {
|
|
98
|
+
const resolvedItem = getCSharpType(program, type.indexer.value, namespace);
|
|
99
|
+
if (resolvedItem === undefined)
|
|
100
|
+
return undefined;
|
|
101
|
+
const { type: itemType, value: _ } = resolvedItem;
|
|
102
|
+
return {
|
|
103
|
+
type: new CSharpType({
|
|
104
|
+
name: `${itemType.name}[]`,
|
|
105
|
+
namespace: itemType.namespace,
|
|
106
|
+
isBuiltIn: itemType.isBuiltIn,
|
|
107
|
+
isValueType: false,
|
|
108
|
+
}),
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
let name = type.name;
|
|
112
|
+
if (isTemplateInstance(type)) {
|
|
113
|
+
name = getModelInstantiationName(program, type, name);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
type: new CSharpType({
|
|
117
|
+
name: ensureCSharpIdentifier(program, type, name, NameCasingType.Class),
|
|
118
|
+
namespace: `${namespace}.Models`,
|
|
119
|
+
isBuiltIn: false,
|
|
120
|
+
isValueType: false,
|
|
121
|
+
}),
|
|
122
|
+
};
|
|
123
|
+
default:
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function coalesceTypes(program, types, namespace) {
|
|
128
|
+
const visited = new Map();
|
|
129
|
+
let candidateType = undefined;
|
|
130
|
+
let candidateValue = undefined;
|
|
131
|
+
for (const type of types) {
|
|
132
|
+
if (!isNullType(type)) {
|
|
133
|
+
if (!visited.has(type)) {
|
|
134
|
+
const resolvedType = getCSharpType(program, type, namespace);
|
|
135
|
+
if (resolvedType === undefined)
|
|
136
|
+
return { type: UnknownType };
|
|
137
|
+
if (resolvedType.type === UnknownType)
|
|
138
|
+
return resolvedType;
|
|
139
|
+
if (candidateType === undefined) {
|
|
140
|
+
candidateType = resolvedType.type;
|
|
141
|
+
candidateValue = resolvedType.value;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
if (candidateValue !== resolvedType.value)
|
|
145
|
+
candidateValue = undefined;
|
|
146
|
+
if (candidateType !== resolvedType.type)
|
|
147
|
+
return { type: UnknownType };
|
|
148
|
+
}
|
|
149
|
+
visited.set(type, resolvedType);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { type: candidateType !== undefined ? candidateType : UnknownType, value: candidateValue };
|
|
154
|
+
}
|
|
155
|
+
export function getKnownType(program, type) {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
export function getCSharpTypeForIntrinsic(program, type) {
|
|
159
|
+
if (isUnknownType(type)) {
|
|
160
|
+
return { type: UnknownType };
|
|
161
|
+
}
|
|
162
|
+
if (isVoidType(type)) {
|
|
163
|
+
return {
|
|
164
|
+
type: new CSharpType({
|
|
165
|
+
name: "void",
|
|
166
|
+
namespace: "System",
|
|
167
|
+
isBuiltIn: true,
|
|
168
|
+
isValueType: false,
|
|
169
|
+
}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (isNullType(type)) {
|
|
173
|
+
return {
|
|
174
|
+
type: new CSharpType({
|
|
175
|
+
name: "object",
|
|
176
|
+
namespace: "System",
|
|
177
|
+
isBuiltIn: true,
|
|
178
|
+
isValueType: false,
|
|
179
|
+
}),
|
|
180
|
+
value: new NullValue(),
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
const standardScalars = new Map([
|
|
186
|
+
[
|
|
187
|
+
"bytes",
|
|
188
|
+
new CSharpType({ name: "byte[]", namespace: "System", isBuiltIn: true, isValueType: false }),
|
|
189
|
+
],
|
|
190
|
+
[
|
|
191
|
+
"int8",
|
|
192
|
+
new CSharpType({ name: "SByte", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
193
|
+
],
|
|
194
|
+
[
|
|
195
|
+
"uint8",
|
|
196
|
+
new CSharpType({ name: "Byte", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
197
|
+
],
|
|
198
|
+
[
|
|
199
|
+
"int16",
|
|
200
|
+
new CSharpType({ name: "Int16", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
201
|
+
],
|
|
202
|
+
[
|
|
203
|
+
"uint16",
|
|
204
|
+
new CSharpType({ name: "UInt16", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
205
|
+
],
|
|
206
|
+
[
|
|
207
|
+
"int16",
|
|
208
|
+
new CSharpType({ name: "Int16", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
209
|
+
],
|
|
210
|
+
[
|
|
211
|
+
"uint16",
|
|
212
|
+
new CSharpType({ name: "UInt16", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
213
|
+
],
|
|
214
|
+
[
|
|
215
|
+
"int32",
|
|
216
|
+
new CSharpType({ name: "int", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
217
|
+
],
|
|
218
|
+
[
|
|
219
|
+
"uint32",
|
|
220
|
+
new CSharpType({ name: "UInt32", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
221
|
+
],
|
|
222
|
+
[
|
|
223
|
+
"integer",
|
|
224
|
+
new CSharpType({ name: "long", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
225
|
+
],
|
|
226
|
+
[
|
|
227
|
+
"int64",
|
|
228
|
+
new CSharpType({ name: "long", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
229
|
+
],
|
|
230
|
+
[
|
|
231
|
+
"uint64",
|
|
232
|
+
new CSharpType({ name: "UInt64", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
233
|
+
],
|
|
234
|
+
[
|
|
235
|
+
"safeint",
|
|
236
|
+
new CSharpType({ name: "long", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
237
|
+
],
|
|
238
|
+
[
|
|
239
|
+
"float",
|
|
240
|
+
new CSharpType({ name: "double", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
241
|
+
],
|
|
242
|
+
[
|
|
243
|
+
"float64",
|
|
244
|
+
new CSharpType({ name: "double", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
245
|
+
],
|
|
246
|
+
[
|
|
247
|
+
"float32",
|
|
248
|
+
new CSharpType({ name: "float", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
249
|
+
],
|
|
250
|
+
[
|
|
251
|
+
"string",
|
|
252
|
+
new CSharpType({ name: "string", namespace: "System", isBuiltIn: true, isValueType: false }),
|
|
253
|
+
],
|
|
254
|
+
[
|
|
255
|
+
"boolean",
|
|
256
|
+
new CSharpType({ name: "bool", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
257
|
+
],
|
|
258
|
+
[
|
|
259
|
+
"plainDate",
|
|
260
|
+
new CSharpType({ name: "DateTime", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
261
|
+
],
|
|
262
|
+
[
|
|
263
|
+
"utcDateTime",
|
|
264
|
+
new CSharpType({
|
|
265
|
+
name: "DateTimeOffset",
|
|
266
|
+
namespace: "System",
|
|
267
|
+
isBuiltIn: true,
|
|
268
|
+
isValueType: true,
|
|
269
|
+
}),
|
|
270
|
+
],
|
|
271
|
+
[
|
|
272
|
+
"offsetDateTime",
|
|
273
|
+
new CSharpType({
|
|
274
|
+
name: "DateTimeOffset",
|
|
275
|
+
namespace: "System",
|
|
276
|
+
isBuiltIn: true,
|
|
277
|
+
isValueType: true,
|
|
278
|
+
}),
|
|
279
|
+
],
|
|
280
|
+
[
|
|
281
|
+
"unixTimestamp32",
|
|
282
|
+
new CSharpType({
|
|
283
|
+
name: "DateTimeOffset",
|
|
284
|
+
namespace: "System",
|
|
285
|
+
isBuiltIn: true,
|
|
286
|
+
isValueType: true,
|
|
287
|
+
}),
|
|
288
|
+
],
|
|
289
|
+
[
|
|
290
|
+
"plainTime",
|
|
291
|
+
new CSharpType({ name: "DateTime", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
292
|
+
],
|
|
293
|
+
[
|
|
294
|
+
"duration",
|
|
295
|
+
new CSharpType({ name: "TimeSpan", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
296
|
+
],
|
|
297
|
+
[
|
|
298
|
+
"numeric",
|
|
299
|
+
new CSharpType({ name: "object", namespace: "System", isBuiltIn: true, isValueType: false }),
|
|
300
|
+
],
|
|
301
|
+
[
|
|
302
|
+
"url",
|
|
303
|
+
new CSharpType({ name: "string", namespace: "System", isBuiltIn: true, isValueType: false }),
|
|
304
|
+
],
|
|
305
|
+
[
|
|
306
|
+
"decimal",
|
|
307
|
+
new CSharpType({ name: "decimal", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
308
|
+
],
|
|
309
|
+
[
|
|
310
|
+
"decimal128",
|
|
311
|
+
new CSharpType({ name: "decimal", namespace: "System", isBuiltIn: true, isValueType: true }),
|
|
312
|
+
],
|
|
313
|
+
]);
|
|
314
|
+
export function getCSharpTypeForStdScalars(program, scalar) {
|
|
315
|
+
const cached = _scalars.get(scalar);
|
|
316
|
+
if (cached !== undefined)
|
|
317
|
+
return cached;
|
|
318
|
+
const builtIn = standardScalars.get(scalar.name);
|
|
319
|
+
if (builtIn !== undefined) {
|
|
320
|
+
_scalars.set(scalar, builtIn);
|
|
321
|
+
if (scalar.name === "numeric" || scalar.name === "integer" || scalar.name === "float") {
|
|
322
|
+
reportDiagnostic(program, {
|
|
323
|
+
code: "no-numeric",
|
|
324
|
+
format: { sourceType: scalar.name, targetType: builtIn?.getTypeReference() },
|
|
325
|
+
target: scalar,
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
return builtIn;
|
|
329
|
+
}
|
|
330
|
+
reportDiagnostic(program, {
|
|
331
|
+
code: "unrecognized-scalar",
|
|
332
|
+
format: { typeName: scalar.name },
|
|
333
|
+
target: scalar,
|
|
334
|
+
});
|
|
335
|
+
return new CSharpType({
|
|
336
|
+
name: "Object",
|
|
337
|
+
namespace: "System",
|
|
338
|
+
isBuiltIn: true,
|
|
339
|
+
isValueType: false,
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
export function isValueType(program, type) {
|
|
343
|
+
if (type.kind === "Boolean" ||
|
|
344
|
+
type.kind === "Number" ||
|
|
345
|
+
type.kind === "Enum" ||
|
|
346
|
+
type.kind === "EnumMember")
|
|
347
|
+
return true;
|
|
348
|
+
if (type.kind === "Scalar")
|
|
349
|
+
return getCSharpTypeForScalar(program, type).isValueType;
|
|
350
|
+
if (type.kind !== "Union")
|
|
351
|
+
return false;
|
|
352
|
+
return [...type.variants.values()]
|
|
353
|
+
.flatMap((v) => v.type)
|
|
354
|
+
.every((t) => isNullType(t) || isValueType(program, t));
|
|
355
|
+
}
|
|
356
|
+
export function formatComment(text, lineLength = 76, lineEnd = "\n") {
|
|
357
|
+
function getNextLine(target) {
|
|
358
|
+
for (let i = lineLength - 1; i > 0; i--) {
|
|
359
|
+
if ([" ", ".", "?", ",", ";"].includes(target.charAt(i))) {
|
|
360
|
+
return `/// ${text.substring(0, i).replaceAll("\n", " ")}`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return `/// ${text.substring(0, lineLength)}`;
|
|
364
|
+
}
|
|
365
|
+
let remaining = text;
|
|
366
|
+
const lines = [];
|
|
367
|
+
while (remaining.length > lineLength) {
|
|
368
|
+
const currentLine = getNextLine(remaining);
|
|
369
|
+
remaining =
|
|
370
|
+
remaining.length > currentLine.length ? remaining.substring(currentLine.length + 1) : "";
|
|
371
|
+
lines.push(currentLine);
|
|
372
|
+
}
|
|
373
|
+
if (remaining.length > 0)
|
|
374
|
+
lines.push(`/// ${remaining}`);
|
|
375
|
+
return `///<summary>${lineEnd}${lines.join(lineEnd)}${lineEnd}///</summary>`;
|
|
376
|
+
}
|
|
377
|
+
export function getCSharpIdentifier(name, context = NameCasingType.Class) {
|
|
378
|
+
if (name === undefined)
|
|
379
|
+
return "Placeholder";
|
|
380
|
+
switch (context) {
|
|
381
|
+
case NameCasingType.Namespace:
|
|
382
|
+
const parts = [];
|
|
383
|
+
for (const part of name.split(".")) {
|
|
384
|
+
parts.push(getCSharpIdentifier(part, NameCasingType.Class));
|
|
385
|
+
}
|
|
386
|
+
return parts.join(".");
|
|
387
|
+
case NameCasingType.Parameter:
|
|
388
|
+
case NameCasingType.Variable:
|
|
389
|
+
return `${camelCase(name)}`;
|
|
390
|
+
default:
|
|
391
|
+
return `${pascalCase(name)}`;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
export function ensureCSharpIdentifier(program, target, name, context = NameCasingType.Class) {
|
|
395
|
+
let location = "";
|
|
396
|
+
switch (target.kind) {
|
|
397
|
+
case "Enum":
|
|
398
|
+
location = `enum ${target.name}`;
|
|
399
|
+
break;
|
|
400
|
+
case "EnumMember":
|
|
401
|
+
location = `enum ${target.enum.name}`;
|
|
402
|
+
break;
|
|
403
|
+
case "Interface":
|
|
404
|
+
location = `interface ${target.name}`;
|
|
405
|
+
break;
|
|
406
|
+
case "Model":
|
|
407
|
+
location = `model ${target.name}`;
|
|
408
|
+
break;
|
|
409
|
+
case "ModelProperty": {
|
|
410
|
+
const model = target.model;
|
|
411
|
+
if (!model) {
|
|
412
|
+
reportDiagnostic(program, {
|
|
413
|
+
code: "missing-type-parent",
|
|
414
|
+
format: { type: "ModelProperty", name: target.name },
|
|
415
|
+
target: target,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
location = `property '${target.name}' in model ${model?.name}`;
|
|
420
|
+
if (!model.name) {
|
|
421
|
+
location = `parameter '${target.name}' in operation`;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
break;
|
|
425
|
+
}
|
|
426
|
+
case "Namespace":
|
|
427
|
+
location = `namespace ${target.name}`;
|
|
428
|
+
let invalid = false;
|
|
429
|
+
const nsName = new StringBuilder();
|
|
430
|
+
for (const part of name.split(".")) {
|
|
431
|
+
if (!isValidCSharpIdentifier(part)) {
|
|
432
|
+
invalid = true;
|
|
433
|
+
nsName.pushLiteralSegment(transformInvalidIdentifier(part));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
if (invalid) {
|
|
437
|
+
reportDiagnostic(program, {
|
|
438
|
+
code: "invalid-identifier",
|
|
439
|
+
format: { identifier: name, location: location },
|
|
440
|
+
target: target.node ?? NoTarget,
|
|
441
|
+
});
|
|
442
|
+
return nsName.segments.join(".");
|
|
443
|
+
}
|
|
444
|
+
return name;
|
|
445
|
+
case "Operation": {
|
|
446
|
+
const parent = target.interface
|
|
447
|
+
? `interface ${target.interface.name}`
|
|
448
|
+
: `namespace ${target.namespace?.name}`;
|
|
449
|
+
location = `operation ${target.name} in ${parent}`;
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
case "Union":
|
|
453
|
+
location = `union ${target.name}`;
|
|
454
|
+
break;
|
|
455
|
+
case "UnionVariant": {
|
|
456
|
+
if (target.node !== undefined) {
|
|
457
|
+
const parent = program.checker.getTypeForNode(target.node.parent);
|
|
458
|
+
if (parent?.kind === "Union")
|
|
459
|
+
location = `variant ${String(target.name)} in union ${parent?.name}`;
|
|
460
|
+
}
|
|
461
|
+
break;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (!isValidCSharpIdentifier(name)) {
|
|
465
|
+
reportDiagnostic(program, {
|
|
466
|
+
code: "invalid-identifier",
|
|
467
|
+
format: { identifier: name, location: location },
|
|
468
|
+
target: target.node ?? NoTarget,
|
|
469
|
+
});
|
|
470
|
+
return getCSharpIdentifier(transformInvalidIdentifier(name), context);
|
|
471
|
+
}
|
|
472
|
+
return getCSharpIdentifier(name, context);
|
|
473
|
+
}
|
|
474
|
+
export function getModelAttributes(program, entity, cSharpName) {
|
|
475
|
+
return getAttributes(program, entity, cSharpName);
|
|
476
|
+
}
|
|
477
|
+
export function getModelDeclarationName(program, model, defaultSuffix) {
|
|
478
|
+
if (model.name !== null && model.name.length > 0) {
|
|
479
|
+
return ensureCSharpIdentifier(program, model, model.name, NameCasingType.Class);
|
|
480
|
+
}
|
|
481
|
+
if (model.sourceModel && model.sourceModel.name && model.sourceModel.name.length > 0) {
|
|
482
|
+
return ensureCSharpIdentifier(program, model, `${model.sourceModel.name}${defaultSuffix}`, NameCasingType.Class);
|
|
483
|
+
}
|
|
484
|
+
if (model.sourceModels.length > 0) {
|
|
485
|
+
const sourceNames = model.sourceModels
|
|
486
|
+
.filter((m) => m.model.name !== undefined && m.model.name.length > 0)
|
|
487
|
+
.flatMap((m) => ensureCSharpIdentifier(program, model, m.model.name, NameCasingType.Class));
|
|
488
|
+
if (sourceNames.length > 0) {
|
|
489
|
+
return `${sourceNames.join()}${defaultSuffix}`;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
return `Model${defaultSuffix}`;
|
|
493
|
+
}
|
|
494
|
+
export function getModelInstantiationName(program, model, name) {
|
|
495
|
+
const friendlyName = getFriendlyName(program, model);
|
|
496
|
+
if (friendlyName && friendlyName.length > 0)
|
|
497
|
+
return friendlyName;
|
|
498
|
+
if (name === undefined || name.length < 1)
|
|
499
|
+
name = ensureCSharpIdentifier(program, model, "", NameCasingType.Class);
|
|
500
|
+
const names = [name];
|
|
501
|
+
if (model.templateMapper !== undefined) {
|
|
502
|
+
for (const paramType of model.templateMapper.args) {
|
|
503
|
+
if (paramType.entityKind === "Type") {
|
|
504
|
+
switch (paramType.kind) {
|
|
505
|
+
case "Enum":
|
|
506
|
+
case "EnumMember":
|
|
507
|
+
case "Model":
|
|
508
|
+
case "ModelProperty":
|
|
509
|
+
case "Namespace":
|
|
510
|
+
case "Scalar":
|
|
511
|
+
case "Union":
|
|
512
|
+
names.push(getCSharpIdentifier(paramType?.name ?? paramType.kind, NameCasingType.Class));
|
|
513
|
+
break;
|
|
514
|
+
default:
|
|
515
|
+
names.push(getCSharpIdentifier(paramType.kind, NameCasingType.Class));
|
|
516
|
+
break;
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return ensureCSharpIdentifier(program, model, names.join(""), NameCasingType.Class);
|
|
522
|
+
}
|
|
523
|
+
export class ModelInfo {
|
|
524
|
+
visited = [];
|
|
525
|
+
getAllProperties(program, model) {
|
|
526
|
+
if (this.visited.includes(model))
|
|
527
|
+
return undefined;
|
|
528
|
+
this.visited.push(model);
|
|
529
|
+
const props = [];
|
|
530
|
+
for (const prop of model.properties.values())
|
|
531
|
+
props.push(prop);
|
|
532
|
+
if (model.baseModel) {
|
|
533
|
+
const additional = this.getAllProperties(program, model.baseModel);
|
|
534
|
+
if (additional !== undefined)
|
|
535
|
+
props.concat(additional);
|
|
536
|
+
}
|
|
537
|
+
return props;
|
|
538
|
+
}
|
|
539
|
+
filterAllProperties(program, model, filter) {
|
|
540
|
+
if (this.visited.includes(model))
|
|
541
|
+
return undefined;
|
|
542
|
+
this.visited.push(model);
|
|
543
|
+
for (const prop of model.properties.values()) {
|
|
544
|
+
if (filter(prop))
|
|
545
|
+
return prop;
|
|
546
|
+
}
|
|
547
|
+
if (model.baseModel !== undefined) {
|
|
548
|
+
return this.filterAllProperties(program, model.baseModel, filter);
|
|
549
|
+
}
|
|
550
|
+
return undefined;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
export function getPropertySource(program, property) {
|
|
554
|
+
let result = property.model;
|
|
555
|
+
while (property.sourceProperty !== undefined) {
|
|
556
|
+
const current = property.sourceProperty;
|
|
557
|
+
result = current.model;
|
|
558
|
+
property = property.sourceProperty;
|
|
559
|
+
}
|
|
560
|
+
return result;
|
|
561
|
+
}
|
|
562
|
+
export function getSourceModel(program, model) {
|
|
563
|
+
const modelTracker = new Set();
|
|
564
|
+
for (const prop of model.properties.values()) {
|
|
565
|
+
const source = getPropertySource(program, prop);
|
|
566
|
+
if (source === undefined)
|
|
567
|
+
return undefined;
|
|
568
|
+
modelTracker.add(source);
|
|
569
|
+
}
|
|
570
|
+
if (modelTracker.size === 1)
|
|
571
|
+
return [...modelTracker.values()][0];
|
|
572
|
+
return undefined;
|
|
573
|
+
}
|
|
574
|
+
export class HttpMetadata {
|
|
575
|
+
resolveLogicalResponseType(program, response) {
|
|
576
|
+
const responseType = response.type;
|
|
577
|
+
const metaInfo = createMetadataInfo(program, {
|
|
578
|
+
canonicalVisibility: Visibility.Read,
|
|
579
|
+
canShareProperty: (p) => true,
|
|
580
|
+
});
|
|
581
|
+
switch (responseType.kind) {
|
|
582
|
+
case "Model":
|
|
583
|
+
if (responseType.indexer && responseType.indexer.key.name !== "string")
|
|
584
|
+
return responseType;
|
|
585
|
+
const bodyProp = new ModelInfo().filterAllProperties(program, responseType, (p) => isBody(program, p) || isBodyRoot(program, p));
|
|
586
|
+
if (bodyProp !== undefined)
|
|
587
|
+
return metaInfo.getEffectivePayloadType(bodyProp.type, Visibility.Read);
|
|
588
|
+
const anyProp = new ModelInfo().filterAllProperties(program, responseType, (p) => !isMetadata(program, p) && !isStatusCode(program, p));
|
|
589
|
+
if (anyProp === undefined)
|
|
590
|
+
return program.checker.voidType;
|
|
591
|
+
if (responseType.name === "") {
|
|
592
|
+
return metaInfo.getEffectivePayloadType(responseType, Visibility.Read);
|
|
593
|
+
}
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
return responseType;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
export function getOperationAttributes(program, entity) {
|
|
600
|
+
return getAttributes(program, entity);
|
|
601
|
+
}
|
|
602
|
+
export function transformInvalidIdentifier(name) {
|
|
603
|
+
const result = new StringBuilder();
|
|
604
|
+
for (let i = 0; i < name.length; ++i) {
|
|
605
|
+
result.pushLiteralSegment(getValidChar(name.charAt(i), i));
|
|
606
|
+
}
|
|
607
|
+
return result.segments.join("");
|
|
608
|
+
}
|
|
609
|
+
export function getOperationVerbDecorator(operation) {
|
|
610
|
+
switch (operation.verb) {
|
|
611
|
+
case "delete":
|
|
612
|
+
return "HttpDelete";
|
|
613
|
+
case "get":
|
|
614
|
+
return "HttpGet";
|
|
615
|
+
case "patch":
|
|
616
|
+
return "HttpPatch";
|
|
617
|
+
case "post":
|
|
618
|
+
return "HttpPost";
|
|
619
|
+
case "put":
|
|
620
|
+
return "HttpPut";
|
|
621
|
+
default:
|
|
622
|
+
return "HttpGet";
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
export function hasNonMetadataProperties(program, model) {
|
|
626
|
+
const props = [...model.properties.values()].filter((p) => !isMetadata(program, p));
|
|
627
|
+
return props.length > 0;
|
|
628
|
+
}
|
|
629
|
+
export async function ensureCleanDirectory(program, targetPath) {
|
|
630
|
+
try {
|
|
631
|
+
await program.host.stat(targetPath);
|
|
632
|
+
await program.host.rm(targetPath, { recursive: true });
|
|
633
|
+
}
|
|
634
|
+
catch { }
|
|
635
|
+
await program.host.mkdirp(targetPath);
|
|
636
|
+
}
|
|
637
|
+
export function isValidCSharpIdentifier(identifier) {
|
|
638
|
+
return identifier?.match(/^[A-Za-z_][\w]*$/) !== null;
|
|
639
|
+
}
|
|
640
|
+
export function getValidChar(target, position) {
|
|
641
|
+
if (position === 0) {
|
|
642
|
+
if (target.match(/[A-Za-z_]/))
|
|
643
|
+
return target;
|
|
644
|
+
return `Generated_${target.match(/\w/) ? target : ""}`;
|
|
645
|
+
}
|
|
646
|
+
if (!target.match(/[\w]/))
|
|
647
|
+
return "_";
|
|
648
|
+
return target;
|
|
649
|
+
}
|
|
650
|
+
export function getCSharpStatusCode(entry) {
|
|
651
|
+
switch (entry) {
|
|
652
|
+
case 200:
|
|
653
|
+
return "HttpStatusCode.OK";
|
|
654
|
+
case 201:
|
|
655
|
+
return "HttpStatusCode.Created";
|
|
656
|
+
case 202:
|
|
657
|
+
return "HttpStatusCode.Accepted";
|
|
658
|
+
case 204:
|
|
659
|
+
return "HttpStatusCode.NoContent";
|
|
660
|
+
default:
|
|
661
|
+
return undefined;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
export function isEmptyResponseModel(program, model) {
|
|
665
|
+
if (model.kind !== "Model")
|
|
666
|
+
return false;
|
|
667
|
+
if (model.properties.size === 0)
|
|
668
|
+
return true;
|
|
669
|
+
return model.properties.size === 1 && isStatusCode(program, [...model.properties.values()][0]);
|
|
670
|
+
}
|
|
671
|
+
export function isContentTypeHeader(program, parameter) {
|
|
672
|
+
return (isHeader(program, parameter) &&
|
|
673
|
+
(parameter.name === "contentType" || getHeaderFieldName(program, parameter) === "Content-type"));
|
|
674
|
+
}
|
|
675
|
+
export function isValidParameter(program, parameter) {
|
|
676
|
+
return (!isContentTypeHeader(program, parameter) &&
|
|
677
|
+
(parameter.type.kind !== "Intrinsic" || parameter.type.name !== "never") &&
|
|
678
|
+
parameter.model?.name === "");
|
|
679
|
+
}
|
|
680
|
+
/** Determine whether the given parameter is http metadata */
|
|
681
|
+
export function isHttpMetadata(program, property) {
|
|
682
|
+
return (isPathParam(program, property) || isHeader(program, property) || isQueryParam(program, property));
|
|
683
|
+
}
|
|
684
|
+
export function getBusinessLogicCallParameters(parameters) {
|
|
685
|
+
const builder = new StringBuilder();
|
|
686
|
+
const blParameters = parameters.filter((p) => p.operationKind === "BusinessLogic" || p.operationKind === "All");
|
|
687
|
+
let i = 0;
|
|
688
|
+
for (const param of blParameters) {
|
|
689
|
+
builder.push(code `${getBusinessLogicCallParameter(param)}${++i < blParameters.length ? ", " : ""}`);
|
|
690
|
+
}
|
|
691
|
+
return builder.reduce();
|
|
692
|
+
}
|
|
693
|
+
export function getBusinessLogicDeclParameters(parameters) {
|
|
694
|
+
const builder = new StringBuilder();
|
|
695
|
+
const blParameters = parameters.filter((p) => p.operationKind === "BusinessLogic" || p.operationKind === "All");
|
|
696
|
+
let i = 0;
|
|
697
|
+
for (const param of blParameters) {
|
|
698
|
+
builder.push(code `${getBusinessLogicSignatureParameter(param)}${++i < blParameters.length ? ", " : ""}`);
|
|
699
|
+
}
|
|
700
|
+
return builder.reduce();
|
|
701
|
+
}
|
|
702
|
+
export function getHttpDeclParameters(parameters) {
|
|
703
|
+
const builder = new StringBuilder();
|
|
704
|
+
const blParameters = parameters.filter((p) => p.operationKind === "Http" || p.operationKind === "All");
|
|
705
|
+
let i = 0;
|
|
706
|
+
for (const param of blParameters) {
|
|
707
|
+
builder.push(code `${getHttpSignatureParameter(param)}${++i < blParameters.length ? ", " : ""}`);
|
|
708
|
+
}
|
|
709
|
+
return builder.reduce();
|
|
710
|
+
}
|
|
711
|
+
export function getBusinessLogicCallParameter(param) {
|
|
712
|
+
const builder = new StringBuilder();
|
|
713
|
+
builder.push(code `${param.callName}`);
|
|
714
|
+
return builder.reduce();
|
|
715
|
+
}
|
|
716
|
+
export function getBusinessLogicSignatureParameter(param) {
|
|
717
|
+
const builder = new StringBuilder();
|
|
718
|
+
builder.push(code `${param.typeName}${param.optional || param.nullable ? "? " : " "}${param.name}`);
|
|
719
|
+
return builder.reduce();
|
|
720
|
+
}
|
|
721
|
+
export function getHttpSignatureParameter(param) {
|
|
722
|
+
const builder = new StringBuilder();
|
|
723
|
+
builder.push(code `${getHttpParameterDecorator(param)}${getBusinessLogicSignatureParameter(param)}${param.defaultValue === undefined ? "" : code ` = ${typeof param.defaultValue === "boolean" ? code `${param.defaultValue.toString()}` : code `${param.defaultValue}`}`}`);
|
|
724
|
+
return builder.reduce();
|
|
725
|
+
}
|
|
726
|
+
export function getHttpParameterDecorator(parameter) {
|
|
727
|
+
switch (parameter.httpParameterKind) {
|
|
728
|
+
case "query":
|
|
729
|
+
return code `[FromQuery${parameter.httpParameterName ? code `(Name="${parameter.httpParameterName}")` : ""}] `;
|
|
730
|
+
case "header":
|
|
731
|
+
return code `[FromHeader${parameter.httpParameterName ? code `(Name="${parameter.httpParameterName}")` : ""}] `;
|
|
732
|
+
default:
|
|
733
|
+
return "";
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
export function getParameterKind(parameter) {
|
|
737
|
+
switch (parameter.type) {
|
|
738
|
+
case "path":
|
|
739
|
+
return "path";
|
|
740
|
+
case "cookie":
|
|
741
|
+
case "header":
|
|
742
|
+
return "header";
|
|
743
|
+
case "query":
|
|
744
|
+
return "query";
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
export function canHaveDefault(program, type) {
|
|
748
|
+
switch (type.kind) {
|
|
749
|
+
case "Boolean":
|
|
750
|
+
case "EnumMember":
|
|
751
|
+
case "Enum":
|
|
752
|
+
case "Number":
|
|
753
|
+
case "String":
|
|
754
|
+
case "Scalar":
|
|
755
|
+
case "StringTemplate":
|
|
756
|
+
return true;
|
|
757
|
+
case "ModelProperty":
|
|
758
|
+
return canHaveDefault(program, type.type);
|
|
759
|
+
default:
|
|
760
|
+
return false;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
export class CSharpOperationHelpers {
|
|
764
|
+
constructor(inEmitter) {
|
|
765
|
+
this.emitter = inEmitter;
|
|
766
|
+
this.#anonymousModels = new Map();
|
|
767
|
+
this.#opCache = new Map();
|
|
768
|
+
}
|
|
769
|
+
emitter;
|
|
770
|
+
#anonymousModels;
|
|
771
|
+
#opCache;
|
|
772
|
+
getParameters(program, operation) {
|
|
773
|
+
function safeConcat(...names) {
|
|
774
|
+
return names
|
|
775
|
+
.filter((n) => n !== undefined && n !== null && n.length > 0)
|
|
776
|
+
.flatMap((s) => getCSharpIdentifier(s, NameCasingType.Class))
|
|
777
|
+
.join();
|
|
778
|
+
}
|
|
779
|
+
const cached = this.#opCache.get(operation.operation);
|
|
780
|
+
if (cached)
|
|
781
|
+
return cached;
|
|
782
|
+
const bodyParam = operation.parameters.body;
|
|
783
|
+
const isExplicitBodyParam = bodyParam?.property !== undefined;
|
|
784
|
+
const result = [];
|
|
785
|
+
const validParams = operation.parameters.parameters.filter((p) => isValidParameter(program, p.param));
|
|
786
|
+
const requiredParams = validParams.filter((p) => p.type === "path" || (!p.param.optional && p.param.defaultValue === undefined));
|
|
787
|
+
const optionalParams = validParams.filter((p) => p.type !== "path" && (p.param.optional || p.param.defaultValue !== undefined));
|
|
788
|
+
for (const parameter of requiredParams) {
|
|
789
|
+
let { typeReference: paramType, defaultValue: paramValue } = this.getTypeInfo(program, parameter.param.type);
|
|
790
|
+
// cSharp does not allow array defaults in operation parameters
|
|
791
|
+
if (!canHaveDefault(program, parameter.param)) {
|
|
792
|
+
paramValue = undefined;
|
|
793
|
+
}
|
|
794
|
+
const paramName = ensureCSharpIdentifier(program, parameter.param, parameter.param.name, NameCasingType.Parameter);
|
|
795
|
+
result.push({
|
|
796
|
+
isExplicitBody: false,
|
|
797
|
+
name: paramName,
|
|
798
|
+
callName: paramName,
|
|
799
|
+
optional: false,
|
|
800
|
+
typeName: paramType,
|
|
801
|
+
defaultValue: paramValue,
|
|
802
|
+
httpParameterKind: getParameterKind(parameter),
|
|
803
|
+
httpParameterName: parameter.name,
|
|
804
|
+
nullable: false,
|
|
805
|
+
operationKind: "All",
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
const overrideParameters = getExplicitBodyParameters(program, operation);
|
|
809
|
+
if (overrideParameters !== undefined) {
|
|
810
|
+
for (const overrideParam of overrideParameters) {
|
|
811
|
+
result.push(overrideParam);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
else if (bodyParam !== undefined && isExplicitBodyParam) {
|
|
815
|
+
let { typeReference: bodyType, defaultValue: bodyValue, nullableType: isNullable, } = this.getTypeInfo(program, bodyParam.type);
|
|
816
|
+
if (!canHaveDefault(program, bodyParam.type)) {
|
|
817
|
+
bodyValue = undefined;
|
|
818
|
+
}
|
|
819
|
+
result.push({
|
|
820
|
+
isExplicitBody: true,
|
|
821
|
+
httpParameterKind: "body",
|
|
822
|
+
name: "body",
|
|
823
|
+
callName: "body",
|
|
824
|
+
typeName: bodyType,
|
|
825
|
+
nullable: isNullable,
|
|
826
|
+
defaultValue: bodyValue,
|
|
827
|
+
optional: bodyParam.property?.optional ?? false,
|
|
828
|
+
operationKind: "All",
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
else if (bodyParam !== undefined) {
|
|
832
|
+
switch (bodyParam.type.kind) {
|
|
833
|
+
case "Model":
|
|
834
|
+
let tsBody = bodyParam.type;
|
|
835
|
+
if (!bodyParam.type.name) {
|
|
836
|
+
tsBody = program.checker.cloneType(bodyParam.type, {
|
|
837
|
+
name: safeConcat(operation.operation.interface?.name, operation.operation.name, "Request"),
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
const { typeReference: bodyType } = this.getTypeInfo(program, tsBody);
|
|
841
|
+
const bodyName = ensureCSharpIdentifier(program, bodyParam.type, "body", NameCasingType.Parameter);
|
|
842
|
+
result.push({
|
|
843
|
+
isExplicitBody: false,
|
|
844
|
+
httpParameterKind: "body",
|
|
845
|
+
name: bodyName,
|
|
846
|
+
callName: bodyName,
|
|
847
|
+
typeName: bodyType,
|
|
848
|
+
nullable: false,
|
|
849
|
+
defaultValue: undefined,
|
|
850
|
+
optional: false,
|
|
851
|
+
operationKind: "Http",
|
|
852
|
+
});
|
|
853
|
+
for (const [propName, propDef] of bodyParam.type.properties) {
|
|
854
|
+
let { typeReference: csType, defaultValue: csValue, nullableType: isNullable, } = this.getTypeInfo(program, propDef.type);
|
|
855
|
+
// cSharp does not allow array defaults in operation parameters
|
|
856
|
+
if (!canHaveDefault(program, propDef)) {
|
|
857
|
+
csValue = undefined;
|
|
858
|
+
}
|
|
859
|
+
const paramName = ensureCSharpIdentifier(program, propDef, propName, NameCasingType.Parameter);
|
|
860
|
+
const refName = ensureCSharpIdentifier(program, propDef, propName, NameCasingType.Property);
|
|
861
|
+
result.push({
|
|
862
|
+
isExplicitBody: false,
|
|
863
|
+
httpParameterKind: "body",
|
|
864
|
+
name: paramName,
|
|
865
|
+
callName: `body.${refName}`,
|
|
866
|
+
typeName: csType,
|
|
867
|
+
nullable: isNullable,
|
|
868
|
+
defaultValue: csValue,
|
|
869
|
+
optional: propDef.optional,
|
|
870
|
+
operationKind: "BusinessLogic",
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
break;
|
|
874
|
+
case "ModelProperty":
|
|
875
|
+
{
|
|
876
|
+
let { typeReference: csType, defaultValue: csValue, nullableType: isNullable, } = this.getTypeInfo(program, bodyParam.type.type);
|
|
877
|
+
if (!canHaveDefault(program, bodyParam.type)) {
|
|
878
|
+
csValue = undefined;
|
|
879
|
+
}
|
|
880
|
+
const optName = ensureCSharpIdentifier(program, bodyParam.type.type, bodyParam.type.name, NameCasingType.Parameter);
|
|
881
|
+
result.push({
|
|
882
|
+
isExplicitBody: true,
|
|
883
|
+
httpParameterKind: "body",
|
|
884
|
+
name: optName,
|
|
885
|
+
callName: optName,
|
|
886
|
+
typeName: csType,
|
|
887
|
+
nullable: isNullable,
|
|
888
|
+
defaultValue: csValue,
|
|
889
|
+
optional: bodyParam.type.optional,
|
|
890
|
+
operationKind: "All",
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
break;
|
|
894
|
+
default: {
|
|
895
|
+
let { typeReference: csType, defaultValue: csValue, nullableType: isNullable, } = this.getTypeInfo(program, bodyParam.type);
|
|
896
|
+
if (!canHaveDefault(program, bodyParam.type)) {
|
|
897
|
+
csValue = undefined;
|
|
898
|
+
}
|
|
899
|
+
result.push({
|
|
900
|
+
isExplicitBody: true,
|
|
901
|
+
httpParameterKind: "body",
|
|
902
|
+
name: "body",
|
|
903
|
+
callName: "body",
|
|
904
|
+
typeName: csType,
|
|
905
|
+
nullable: isNullable,
|
|
906
|
+
defaultValue: csValue,
|
|
907
|
+
optional: false,
|
|
908
|
+
operationKind: "All",
|
|
909
|
+
});
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
for (const parameter of optionalParams) {
|
|
914
|
+
const { typeReference: paramType, defaultValue: paramValue, nullableType: isNullable, } = this.getTypeInfo(program, parameter.param.type);
|
|
915
|
+
const optName = ensureCSharpIdentifier(program, parameter.param, parameter.param.name, NameCasingType.Parameter);
|
|
916
|
+
result.push({
|
|
917
|
+
isExplicitBody: false,
|
|
918
|
+
name: optName,
|
|
919
|
+
callName: optName,
|
|
920
|
+
optional: true,
|
|
921
|
+
typeName: paramType,
|
|
922
|
+
defaultValue: paramValue,
|
|
923
|
+
httpParameterKind: getParameterKind(parameter),
|
|
924
|
+
httpParameterName: parameter.name,
|
|
925
|
+
nullable: isNullable,
|
|
926
|
+
operationKind: "All",
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
this.#opCache.set(operation.operation, result);
|
|
930
|
+
return result;
|
|
931
|
+
}
|
|
932
|
+
getTypeInfo(program, tsType) {
|
|
933
|
+
const myEmitter = this.emitter;
|
|
934
|
+
function extractStringValue(type, span) {
|
|
935
|
+
switch (type.kind) {
|
|
936
|
+
case "String":
|
|
937
|
+
return type.value;
|
|
938
|
+
case "Boolean":
|
|
939
|
+
return `${type.value}`;
|
|
940
|
+
case "Number":
|
|
941
|
+
return type.valueAsString;
|
|
942
|
+
case "StringTemplateSpan":
|
|
943
|
+
if (type.isInterpolated) {
|
|
944
|
+
return extractStringValue(type.type, span);
|
|
945
|
+
}
|
|
946
|
+
else {
|
|
947
|
+
return type.type.value;
|
|
948
|
+
}
|
|
949
|
+
case "ModelProperty":
|
|
950
|
+
return extractStringValue(type.type, span);
|
|
951
|
+
case "EnumMember":
|
|
952
|
+
if (type.value === undefined)
|
|
953
|
+
return type.name;
|
|
954
|
+
if (typeof type.value === "string")
|
|
955
|
+
return type.value;
|
|
956
|
+
if (typeof type.value === "number")
|
|
957
|
+
return `${type.value}`;
|
|
958
|
+
}
|
|
959
|
+
reportDiagnostic(myEmitter.getProgram(), {
|
|
960
|
+
code: "invalid-interpolation",
|
|
961
|
+
target: span,
|
|
962
|
+
format: {},
|
|
963
|
+
});
|
|
964
|
+
return "";
|
|
965
|
+
}
|
|
966
|
+
switch (tsType.kind) {
|
|
967
|
+
case "String":
|
|
968
|
+
return {
|
|
969
|
+
typeReference: code `string`,
|
|
970
|
+
defaultValue: `"${tsType.value}"`,
|
|
971
|
+
nullableType: false,
|
|
972
|
+
};
|
|
973
|
+
case "StringTemplate":
|
|
974
|
+
const template = tsType;
|
|
975
|
+
if (template.stringValue !== undefined)
|
|
976
|
+
return {
|
|
977
|
+
typeReference: code `string`,
|
|
978
|
+
defaultValue: `"${template.stringValue}"`,
|
|
979
|
+
nullableType: false,
|
|
980
|
+
};
|
|
981
|
+
const spanResults = [];
|
|
982
|
+
for (const span of template.spans) {
|
|
983
|
+
spanResults.push(extractStringValue(span, span));
|
|
984
|
+
}
|
|
985
|
+
return {
|
|
986
|
+
typeReference: code `string`,
|
|
987
|
+
defaultValue: `"${spanResults.join("")}"`,
|
|
988
|
+
nullableType: false,
|
|
989
|
+
};
|
|
990
|
+
case "Boolean":
|
|
991
|
+
return {
|
|
992
|
+
typeReference: code `bool`,
|
|
993
|
+
defaultValue: `${tsType.value === true ? true : false}`,
|
|
994
|
+
nullableType: false,
|
|
995
|
+
};
|
|
996
|
+
case "Number":
|
|
997
|
+
const [type, value] = findNumericType(tsType);
|
|
998
|
+
return { typeReference: code `${type}`, defaultValue: `${value}`, nullableType: false };
|
|
999
|
+
case "Tuple":
|
|
1000
|
+
const defaults = [];
|
|
1001
|
+
const [csharpType, isObject] = coalesceTsTypes(program, tsType.values);
|
|
1002
|
+
if (isObject)
|
|
1003
|
+
return { typeReference: "object[]", defaultValue: undefined, nullableType: false };
|
|
1004
|
+
for (const value of tsType.values) {
|
|
1005
|
+
const { defaultValue: itemDefault } = this.getTypeInfo(program, value);
|
|
1006
|
+
defaults.push(itemDefault);
|
|
1007
|
+
}
|
|
1008
|
+
return {
|
|
1009
|
+
typeReference: code `${csharpType.getTypeReference()}[]`,
|
|
1010
|
+
defaultValue: `[${defaults.join(", ")}]`,
|
|
1011
|
+
nullableType: csharpType.isNullable,
|
|
1012
|
+
};
|
|
1013
|
+
case "Object":
|
|
1014
|
+
return { typeReference: code `object`, defaultValue: undefined, nullableType: false };
|
|
1015
|
+
case "Model":
|
|
1016
|
+
let modelResult;
|
|
1017
|
+
const cachedResult = this.#anonymousModels.get(tsType);
|
|
1018
|
+
if (cachedResult) {
|
|
1019
|
+
return cachedResult;
|
|
1020
|
+
}
|
|
1021
|
+
if (isRecord(tsType)) {
|
|
1022
|
+
modelResult = { typeReference: code `JsonObject`, nullableType: false };
|
|
1023
|
+
}
|
|
1024
|
+
else {
|
|
1025
|
+
modelResult = {
|
|
1026
|
+
typeReference: code `${this.emitter.emitTypeReference(tsType)}`,
|
|
1027
|
+
nullableType: false,
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
this.#anonymousModels.set(tsType, modelResult);
|
|
1031
|
+
return modelResult;
|
|
1032
|
+
case "ModelProperty":
|
|
1033
|
+
return this.getTypeInfo(program, tsType.type);
|
|
1034
|
+
case "Enum":
|
|
1035
|
+
return {
|
|
1036
|
+
typeReference: code `${this.emitter.emitTypeReference(tsType)}`,
|
|
1037
|
+
nullableType: false,
|
|
1038
|
+
};
|
|
1039
|
+
case "EnumMember":
|
|
1040
|
+
if (typeof tsType.value === "number") {
|
|
1041
|
+
const stringValue = tsType.value.toString();
|
|
1042
|
+
if (stringValue.includes(".") || stringValue.includes("e"))
|
|
1043
|
+
return { typeReference: "double", defaultValue: stringValue, nullableType: false };
|
|
1044
|
+
return { typeReference: "int", defaultValue: stringValue, nullableType: false };
|
|
1045
|
+
}
|
|
1046
|
+
if (typeof tsType.value === "string") {
|
|
1047
|
+
return { typeReference: "string", defaultValue: tsType.value, nullableType: false };
|
|
1048
|
+
}
|
|
1049
|
+
return { typeReference: code `object`, nullableType: false };
|
|
1050
|
+
case "Union":
|
|
1051
|
+
return this.getUnionInfo(program, tsType);
|
|
1052
|
+
case "UnionVariant":
|
|
1053
|
+
return this.getTypeInfo(program, tsType.type);
|
|
1054
|
+
default:
|
|
1055
|
+
return {
|
|
1056
|
+
typeReference: code `${this.emitter.emitTypeReference(tsType)}`,
|
|
1057
|
+
nullableType: false,
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
getUnionInfo(program, union) {
|
|
1062
|
+
const propResult = getNonNullableTsType(union);
|
|
1063
|
+
if (propResult === undefined) {
|
|
1064
|
+
return {
|
|
1065
|
+
typeReference: code `${this.emitter.emitTypeReference(union)}`,
|
|
1066
|
+
nullableType: [...union.variants.values()].some((v) => isNullType(v.type)),
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
const candidate = this.getTypeInfo(program, propResult.type);
|
|
1070
|
+
candidate.nullableType = propResult.nullable;
|
|
1071
|
+
return candidate;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
export function getExplicitBodyParameters(program, httpOperation) {
|
|
1075
|
+
if (httpOperation.parameters.body && httpOperation.parameters.body.bodyKind === "multipart") {
|
|
1076
|
+
return [
|
|
1077
|
+
{
|
|
1078
|
+
name: "reader",
|
|
1079
|
+
callName: "reader",
|
|
1080
|
+
nullable: false,
|
|
1081
|
+
optional: false,
|
|
1082
|
+
typeName: "MultipartReader",
|
|
1083
|
+
isExplicitBody: false,
|
|
1084
|
+
httpParameterKind: "body",
|
|
1085
|
+
operationKind: "BusinessLogic",
|
|
1086
|
+
},
|
|
1087
|
+
];
|
|
1088
|
+
}
|
|
1089
|
+
return undefined;
|
|
1090
|
+
}
|
|
1091
|
+
export function findNumericType(type) {
|
|
1092
|
+
const stringValue = type.valueAsString;
|
|
1093
|
+
if (stringValue.includes(".") || stringValue.includes("e"))
|
|
1094
|
+
return ["double", stringValue];
|
|
1095
|
+
return ["int", stringValue];
|
|
1096
|
+
}
|
|
1097
|
+
export function coalesceUnionTypes(program, union) {
|
|
1098
|
+
const [result, _] = coalesceTsTypes(program, [...union.variants.values()].flatMap((v) => v.type));
|
|
1099
|
+
return result;
|
|
1100
|
+
}
|
|
1101
|
+
export function getNonNullableTsType(union) {
|
|
1102
|
+
const types = [...union.variants.values()];
|
|
1103
|
+
const nulls = types.flatMap((v) => v.type).filter((t) => isNullType(t));
|
|
1104
|
+
const nonNulls = types.flatMap((v) => v.type).filter((t) => !isNullType(t));
|
|
1105
|
+
if (nonNulls.length === 1)
|
|
1106
|
+
return { type: nonNulls[0], nullable: nulls.length > 0 };
|
|
1107
|
+
return undefined;
|
|
1108
|
+
}
|
|
1109
|
+
export function coalesceTsTypes(program, types) {
|
|
1110
|
+
const defaultValue = [
|
|
1111
|
+
new CSharpType({
|
|
1112
|
+
name: "object",
|
|
1113
|
+
namespace: "System",
|
|
1114
|
+
isBuiltIn: true,
|
|
1115
|
+
isValueType: false,
|
|
1116
|
+
}),
|
|
1117
|
+
true,
|
|
1118
|
+
];
|
|
1119
|
+
let current = undefined;
|
|
1120
|
+
let nullable = false;
|
|
1121
|
+
for (const type of types) {
|
|
1122
|
+
let candidate = undefined;
|
|
1123
|
+
switch (type.kind) {
|
|
1124
|
+
case "Boolean":
|
|
1125
|
+
candidate = new CSharpType({ name: "bool", namespace: "System", isValueType: true });
|
|
1126
|
+
break;
|
|
1127
|
+
case "StringTemplate":
|
|
1128
|
+
case "String":
|
|
1129
|
+
candidate = new CSharpType({ name: "string", namespace: "System", isValueType: false });
|
|
1130
|
+
break;
|
|
1131
|
+
case "Number":
|
|
1132
|
+
const stringValue = type.valueAsString;
|
|
1133
|
+
if (stringValue.includes(".") || stringValue.includes("e")) {
|
|
1134
|
+
candidate = new CSharpType({
|
|
1135
|
+
name: "double",
|
|
1136
|
+
namespace: "System",
|
|
1137
|
+
isValueType: true,
|
|
1138
|
+
});
|
|
1139
|
+
}
|
|
1140
|
+
else {
|
|
1141
|
+
candidate = new CSharpType({ name: "int", namespace: "System", isValueType: true });
|
|
1142
|
+
}
|
|
1143
|
+
break;
|
|
1144
|
+
case "Union":
|
|
1145
|
+
candidate = coalesceUnionTypes(program, type);
|
|
1146
|
+
break;
|
|
1147
|
+
case "Scalar":
|
|
1148
|
+
candidate = getCSharpTypeForScalar(program, type);
|
|
1149
|
+
break;
|
|
1150
|
+
case "Intrinsic":
|
|
1151
|
+
if (isNullType(type)) {
|
|
1152
|
+
nullable = true;
|
|
1153
|
+
candidate = current;
|
|
1154
|
+
}
|
|
1155
|
+
else {
|
|
1156
|
+
return defaultValue;
|
|
1157
|
+
}
|
|
1158
|
+
break;
|
|
1159
|
+
default:
|
|
1160
|
+
return defaultValue;
|
|
1161
|
+
}
|
|
1162
|
+
current = current ?? candidate;
|
|
1163
|
+
if (current === undefined || (candidate !== undefined && !candidate.equals(current)))
|
|
1164
|
+
return defaultValue;
|
|
1165
|
+
}
|
|
1166
|
+
if (current !== undefined && nullable)
|
|
1167
|
+
current.isNullable = true;
|
|
1168
|
+
return current === undefined ? defaultValue : [current, false];
|
|
1169
|
+
}
|
|
1170
|
+
export function isRecord(type) {
|
|
1171
|
+
return type.kind === "Model" && type.name === "Record" && type.indexer !== undefined;
|
|
1172
|
+
}
|
|
1173
|
+
//# sourceMappingURL=utils.js.map
|