@typespec/http-server-csharp 0.58.0-alpha.2-dev.1 → 0.58.0-alpha.20-dev.0
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 +69 -6
- 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 +173 -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} +113 -35
- package/dist/src/lib/attributes.js.map +1 -0
- package/dist/src/lib/boilerplate.d.ts +6 -0
- package/dist/src/lib/boilerplate.d.ts.map +1 -0
- package/dist/src/{boilerplate.js → lib/boilerplate.js} +253 -66
- package/dist/src/lib/boilerplate.js.map +1 -0
- package/dist/src/lib/doc.d.ts +5 -0
- package/dist/src/lib/doc.d.ts.map +1 -0
- package/dist/src/lib/doc.js +237 -0
- package/dist/src/lib/doc.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} +58 -4
- package/dist/src/lib/interfaces.d.ts.map +1 -0
- package/dist/src/{interfaces.js → lib/interfaces.js} +100 -27
- package/dist/src/lib/interfaces.js.map +1 -0
- package/dist/src/{lib.d.ts → lib/lib.d.ts} +54 -1
- package/dist/src/lib/lib.d.ts.map +1 -0
- package/dist/src/lib/lib.js +146 -0
- package/dist/src/lib/lib.js.map +1 -0
- package/dist/src/lib/project.d.ts +5 -0
- package/dist/src/lib/project.d.ts.map +1 -0
- package/dist/src/lib/project.js +101 -0
- package/dist/src/lib/project.js.map +1 -0
- package/dist/src/lib/scaffolding.d.ts +22 -0
- package/dist/src/lib/scaffolding.d.ts.map +1 -0
- package/dist/src/lib/scaffolding.js +461 -0
- package/dist/src/lib/scaffolding.js.map +1 -0
- package/dist/src/lib/service.d.ts.map +1 -0
- package/dist/src/lib/service.js +1188 -0
- 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/{type-helpers.d.ts → lib/type-helpers.d.ts} +5 -1
- package/dist/src/lib/type-helpers.d.ts.map +1 -0
- package/dist/src/{type-helpers.js → lib/type-helpers.js} +31 -0
- package/dist/src/lib/type-helpers.js.map +1 -0
- package/dist/src/lib/utils.d.ts +114 -0
- package/dist/src/lib/utils.d.ts.map +1 -0
- package/dist/src/lib/utils.js +1557 -0
- package/dist/src/lib/utils.js.map +1 -0
- package/package.json +49 -27
- package/dist/src/attributes.d.ts.map +0 -1
- package/dist/src/attributes.js.map +0 -1
- package/dist/src/boilerplate.d.ts +0 -4
- 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 +0 -60
- package/dist/src/lib.js.map +0 -1
- package/dist/src/service.d.ts.map +0 -1
- package/dist/src/service.js +0 -829
- 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/service.js
DELETED
|
@@ -1,829 +0,0 @@
|
|
|
1
|
-
import { getDoc, getNamespaceFullName, getService, isErrorModel, isNeverType, isNullType, isVoidType, } from "@typespec/compiler";
|
|
2
|
-
import { CodeTypeEmitter, Placeholder, StringBuilder, code, createAssetEmitter, } from "@typespec/compiler/emitter-framework";
|
|
3
|
-
import { Visibility, createMetadataInfo, getHttpOperation, isStatusCode, } from "@typespec/http";
|
|
4
|
-
import { getResourceOperation } from "@typespec/rest";
|
|
5
|
-
import { execFile } from "child_process";
|
|
6
|
-
import { getSerializationSourceFiles } from "./boilerplate.js";
|
|
7
|
-
import { CSharpSourceType, CSharpType, NameCasingType, } from "./interfaces.js";
|
|
8
|
-
import { reportDiagnostic } from "./lib.js";
|
|
9
|
-
import { getRecordType, isKnownReferenceType } from "./type-helpers.js";
|
|
10
|
-
import { HttpMetadata, UnknownType, coalesceTypes, ensureCSharpIdentifier, ensureCleanDirectory, formatComment, getCSharpIdentifier, getCSharpStatusCode, getCSharpType, getCSharpTypeForScalar, getModelAttributes, getModelInstantiationName, getOperationVerbDecorator, isValueType, } from "./utils.js";
|
|
11
|
-
export async function $onEmit(context) {
|
|
12
|
-
let _unionCounter = 0;
|
|
13
|
-
const controllers = new Map();
|
|
14
|
-
const NoResourceContext = "RPCOperations";
|
|
15
|
-
class CSharpCodeEmitter extends CodeTypeEmitter {
|
|
16
|
-
#metadateMap = new Map();
|
|
17
|
-
#licenseHeader = `// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
18
|
-
// Licensed under the MIT License.`;
|
|
19
|
-
#sourceTypeKey = "sourceType";
|
|
20
|
-
#libraryFiles = getSerializationSourceFiles(this.emitter);
|
|
21
|
-
#baseNamespace = undefined;
|
|
22
|
-
#emitterOutputType = context.options["output-type"];
|
|
23
|
-
#metaInfo = createMetadataInfo(this.emitter.getProgram(), {
|
|
24
|
-
canonicalVisibility: Visibility.Read,
|
|
25
|
-
canShareProperty: (p) => true,
|
|
26
|
-
});
|
|
27
|
-
arrayDeclaration(array, name, elementType) {
|
|
28
|
-
return this.emitter.result.declaration(ensureCSharpIdentifier(this.emitter.getProgram(), array, name), code `${this.emitter.emitTypeReference(elementType)}[]`);
|
|
29
|
-
}
|
|
30
|
-
arrayLiteral(array, elementType) {
|
|
31
|
-
return this.emitter.result.rawCode(code `${this.emitter.emitTypeReference(elementType)}[]`);
|
|
32
|
-
}
|
|
33
|
-
booleanLiteral(boolean) {
|
|
34
|
-
return this.emitter.result.rawCode(code `${boolean.value === true ? "true" : "false"}`);
|
|
35
|
-
}
|
|
36
|
-
unionLiteral(union) {
|
|
37
|
-
const csType = this.#coalesceUnionTypes(union);
|
|
38
|
-
return this.emitter.result.rawCode(csType && csType.isBuiltIn ? csType.name : "object");
|
|
39
|
-
}
|
|
40
|
-
declarationName(declarationType) {
|
|
41
|
-
switch (declarationType.kind) {
|
|
42
|
-
case "Enum":
|
|
43
|
-
case "Interface":
|
|
44
|
-
case "Model":
|
|
45
|
-
case "Operation":
|
|
46
|
-
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
47
|
-
case "Union":
|
|
48
|
-
if (!declarationType.name)
|
|
49
|
-
return `Union${_unionCounter++}`;
|
|
50
|
-
return getCSharpIdentifier(declarationType.name, NameCasingType.Class);
|
|
51
|
-
case "Scalar":
|
|
52
|
-
default:
|
|
53
|
-
return getCSharpIdentifier(declarationType.name, NameCasingType.Variable);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
enumDeclaration(en, name) {
|
|
57
|
-
const program = this.emitter.getProgram();
|
|
58
|
-
const enumName = ensureCSharpIdentifier(program, en, name);
|
|
59
|
-
const namespace = this.emitter.getContext().namespace;
|
|
60
|
-
const doc = getDoc(this.emitter.getProgram(), en);
|
|
61
|
-
const attributes = getModelAttributes(program, en, enumName);
|
|
62
|
-
this.#metadateMap.set(en, new CSharpType({ name: enumName, namespace: namespace }));
|
|
63
|
-
return this.emitter.result.declaration(enumName, code `${this.#licenseHeader}
|
|
64
|
-
// <auto-generated />
|
|
65
|
-
|
|
66
|
-
${this.#emitUsings()}
|
|
67
|
-
|
|
68
|
-
namespace ${namespace}
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
${doc ? `${formatComment(doc)}` : ""}
|
|
72
|
-
${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}
|
|
73
|
-
public enum ${enumName}
|
|
74
|
-
{
|
|
75
|
-
${this.emitter.emitEnumMembers(en)};
|
|
76
|
-
}
|
|
77
|
-
} `);
|
|
78
|
-
}
|
|
79
|
-
enumDeclarationContext(en) {
|
|
80
|
-
const enumName = ensureCSharpIdentifier(this.emitter.getProgram(), en, en.name);
|
|
81
|
-
const enumFile = this.emitter.createSourceFile(`models/${enumName}.cs`);
|
|
82
|
-
enumFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model;
|
|
83
|
-
const enumNamespace = `${this.#getOrSetBaseNamespace(en)}.Models`;
|
|
84
|
-
return {
|
|
85
|
-
namespace: enumNamespace,
|
|
86
|
-
file: enumFile,
|
|
87
|
-
scope: enumFile.globalScope,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
enumMembers(en) {
|
|
91
|
-
const result = new StringBuilder();
|
|
92
|
-
let i = 0;
|
|
93
|
-
for (const [name, member] of en.members) {
|
|
94
|
-
i++;
|
|
95
|
-
const memberName = ensureCSharpIdentifier(this.emitter.getProgram(), member, name);
|
|
96
|
-
this.#metadateMap.set(member, { name: memberName });
|
|
97
|
-
result.push(code `${ensureCSharpIdentifier(this.emitter.getProgram(), member, name)} = "${member.value ? member.value : name}"`);
|
|
98
|
-
if (i < en.members.size)
|
|
99
|
-
result.pushLiteralSegment(", ");
|
|
100
|
-
}
|
|
101
|
-
return this.emitter.result.rawCode(result.reduce());
|
|
102
|
-
}
|
|
103
|
-
intrinsic(intrinsic, name) {
|
|
104
|
-
switch (intrinsic.name) {
|
|
105
|
-
case "unknown":
|
|
106
|
-
return this.emitter.result.rawCode(code `System.Text.Json.Nodes.JsonNode`);
|
|
107
|
-
case "null":
|
|
108
|
-
return this.emitter.result.rawCode(code `System.Text.Json.Nodes.JsonNode`);
|
|
109
|
-
case "ErrorType":
|
|
110
|
-
case "never":
|
|
111
|
-
case "void":
|
|
112
|
-
reportDiagnostic(this.emitter.getProgram(), {
|
|
113
|
-
code: "invalid-intrinsic",
|
|
114
|
-
target: intrinsic,
|
|
115
|
-
format: { typeName: intrinsic.name },
|
|
116
|
-
});
|
|
117
|
-
return "";
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
#emitUsings(file) {
|
|
121
|
-
const builder = new StringBuilder();
|
|
122
|
-
if (file === undefined) {
|
|
123
|
-
file = this.emitter.getContext().file;
|
|
124
|
-
}
|
|
125
|
-
for (const ns of [...file.imports.keys()])
|
|
126
|
-
builder.pushLiteralSegment(`using ${ns};`);
|
|
127
|
-
return builder.segments.join("\n");
|
|
128
|
-
}
|
|
129
|
-
modelDeclaration(model, name) {
|
|
130
|
-
const className = ensureCSharpIdentifier(this.emitter.getProgram(), model, name);
|
|
131
|
-
const namespace = this.emitter.getContext().namespace;
|
|
132
|
-
const doc = getDoc(this.emitter.getProgram(), model);
|
|
133
|
-
const attributes = getModelAttributes(this.emitter.getProgram(), model, className);
|
|
134
|
-
this.#metadateMap.set(model, new CSharpType({ name: className, namespace: namespace }));
|
|
135
|
-
const decl = this.emitter.result.declaration(className, code `${this.#licenseHeader}
|
|
136
|
-
// <auto-generated />
|
|
137
|
-
|
|
138
|
-
${this.#emitUsings()}
|
|
139
|
-
|
|
140
|
-
namespace ${namespace} {
|
|
141
|
-
|
|
142
|
-
${doc ? `${formatComment(doc)}\n` : ""}${`${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}${attributes?.length > 0 ? "\n" : ""}`}public partial class ${className} ${model.baseModel ? code `: ${this.emitter.emitTypeReference(model.baseModel)}` : ""} {
|
|
143
|
-
${this.emitter.emitModelProperties(model)}
|
|
144
|
-
}
|
|
145
|
-
} `);
|
|
146
|
-
return decl;
|
|
147
|
-
}
|
|
148
|
-
modelDeclarationContext(model, name) {
|
|
149
|
-
const modelName = ensureCSharpIdentifier(this.emitter.getProgram(), model, name);
|
|
150
|
-
const modelFile = this.emitter.createSourceFile(`models/${modelName}.cs`);
|
|
151
|
-
modelFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model;
|
|
152
|
-
const modelNamespace = `${this.#getOrSetBaseNamespace(model)}.Models`;
|
|
153
|
-
return this.#createModelContext(modelNamespace, modelFile);
|
|
154
|
-
}
|
|
155
|
-
modelInstantiationContext(model) {
|
|
156
|
-
const modelName = getModelInstantiationName(this.emitter.getProgram(), model, model.name);
|
|
157
|
-
const sourceFile = this.emitter.createSourceFile(`models/${modelName}.cs`);
|
|
158
|
-
sourceFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model;
|
|
159
|
-
const modelNamespace = `${this.#getOrSetBaseNamespace(model)}.Models`;
|
|
160
|
-
const context = this.#createModelContext(modelNamespace, sourceFile);
|
|
161
|
-
context.instantiationName = modelName;
|
|
162
|
-
return context;
|
|
163
|
-
}
|
|
164
|
-
modelInstantiation(model, name) {
|
|
165
|
-
const program = this.emitter.getProgram();
|
|
166
|
-
const recordType = getRecordType(program, model);
|
|
167
|
-
if (recordType !== undefined) {
|
|
168
|
-
return code `Dictionary<string, ${this.emitter.emitTypeReference(recordType)}>`;
|
|
169
|
-
}
|
|
170
|
-
const context = this.emitter.getContext();
|
|
171
|
-
const className = context.instantiationName ?? name;
|
|
172
|
-
return this.modelDeclaration(model, className);
|
|
173
|
-
}
|
|
174
|
-
modelProperties(model) {
|
|
175
|
-
const result = new StringBuilder();
|
|
176
|
-
for (const [_, prop] of model.properties) {
|
|
177
|
-
if (!isVoidType(prop.type) &&
|
|
178
|
-
!isNeverType(prop.type) &&
|
|
179
|
-
!isNullType(prop.type) &&
|
|
180
|
-
!isErrorModel(this.emitter.getProgram(), prop.type))
|
|
181
|
-
result.push(code `${this.emitter.emitModelProperty(prop)}`);
|
|
182
|
-
}
|
|
183
|
-
return result.reduce();
|
|
184
|
-
}
|
|
185
|
-
#isRecord(type) {
|
|
186
|
-
return type.kind === "Model" && type.name === "Record" && type.indexer !== undefined;
|
|
187
|
-
}
|
|
188
|
-
modelPropertyLiteral(property) {
|
|
189
|
-
if (isStatusCode(this.emitter.getProgram(), property))
|
|
190
|
-
return "";
|
|
191
|
-
const propertyName = ensureCSharpIdentifier(this.emitter.getProgram(), property, property.name);
|
|
192
|
-
const [typeName, typeDefault] = this.#findPropertyType(property);
|
|
193
|
-
const doc = getDoc(this.emitter.getProgram(), property);
|
|
194
|
-
const attributes = getModelAttributes(this.emitter.getProgram(), property);
|
|
195
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
196
|
-
const defaultValue = property.default
|
|
197
|
-
? // eslint-disable-next-line deprecation/deprecation
|
|
198
|
-
code `${this.emitter.emitType(property.default)}`
|
|
199
|
-
: typeDefault;
|
|
200
|
-
return this.emitter.result
|
|
201
|
-
.rawCode(code `${doc ? `${formatComment(doc)}\n` : ""}${`${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}${attributes?.length > 0 ? "\n" : ""}`}public ${typeName}${property.optional && isValueType(this.emitter.getProgram(), property.type) ? "?" : ""} ${propertyName} { get; ${typeDefault ? "}" : "set; }"}${defaultValue ? ` = ${defaultValue};\n` : "\n"}
|
|
202
|
-
`);
|
|
203
|
-
}
|
|
204
|
-
#findPropertyType(property) {
|
|
205
|
-
switch (property.type.kind) {
|
|
206
|
-
case "String":
|
|
207
|
-
return [code `string`, `"${property.type.value}"`];
|
|
208
|
-
case "Boolean":
|
|
209
|
-
return [code `bool`, `${property.type.value === true ? true : false}`];
|
|
210
|
-
case "Number":
|
|
211
|
-
case "Object":
|
|
212
|
-
return [code `object`, undefined];
|
|
213
|
-
case "Model":
|
|
214
|
-
if (this.#isRecord(property.type)) {
|
|
215
|
-
return [code `JsonObject`, undefined];
|
|
216
|
-
}
|
|
217
|
-
return [code `${this.emitter.emitTypeReference(property.type)}`, undefined];
|
|
218
|
-
default:
|
|
219
|
-
return [code `${this.emitter.emitTypeReference(property.type)}`, undefined];
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
modelPropertyReference(property) {
|
|
223
|
-
return code `${this.emitter.emitTypeReference(property.type)}`;
|
|
224
|
-
}
|
|
225
|
-
numericLiteral(number) {
|
|
226
|
-
return this.emitter.result.rawCode(code `${number.value.toString()}`);
|
|
227
|
-
}
|
|
228
|
-
interfaceDeclaration(iface, name) {
|
|
229
|
-
// interface declaration
|
|
230
|
-
const ifaceName = `I${ensureCSharpIdentifier(this.emitter.getProgram(), iface, name, NameCasingType.Class)}`;
|
|
231
|
-
const namespace = this.emitter.getContext().namespace;
|
|
232
|
-
const doc = getDoc(this.emitter.getProgram(), iface);
|
|
233
|
-
const attributes = getModelAttributes(this.emitter.getProgram(), iface, ifaceName);
|
|
234
|
-
this.#metadateMap.set(iface, new CSharpType({ name: ifaceName, namespace: namespace }));
|
|
235
|
-
const decl = this.emitter.result.declaration(ifaceName, code `${this.#licenseHeader}
|
|
236
|
-
// <auto-generated />
|
|
237
|
-
|
|
238
|
-
${this.#emitUsings()}
|
|
239
|
-
|
|
240
|
-
namespace ${namespace} {
|
|
241
|
-
|
|
242
|
-
${doc ? `${formatComment(doc)}\n` : ""}${`${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}${attributes?.length > 0 ? "\n" : ""}`}public interface ${ifaceName} {
|
|
243
|
-
${this.emitter.emitInterfaceOperations(iface)}
|
|
244
|
-
}
|
|
245
|
-
} `);
|
|
246
|
-
return decl;
|
|
247
|
-
}
|
|
248
|
-
interfaceDeclarationContext(iface, name) {
|
|
249
|
-
// set up interfaces file for declaration
|
|
250
|
-
const ifaceName = `I${ensureCSharpIdentifier(this.emitter.getProgram(), iface, name, NameCasingType.Class)}`;
|
|
251
|
-
const sourceFile = this.emitter.createSourceFile(`operations/${ifaceName}.cs`);
|
|
252
|
-
sourceFile.meta[this.#sourceTypeKey] = CSharpSourceType.Interface;
|
|
253
|
-
const ifaceNamespace = this.#getOrSetBaseNamespace(iface);
|
|
254
|
-
const modelNamespace = `${ifaceNamespace}.Models`;
|
|
255
|
-
const context = this.#createModelContext(ifaceNamespace, sourceFile);
|
|
256
|
-
context.file.imports.set("System", ["System"]);
|
|
257
|
-
context.file.imports.set("System.Net", ["System.Net"]);
|
|
258
|
-
context.file.imports.set("System.Text.Json", ["System.Text.Json"]);
|
|
259
|
-
context.file.imports.set("System.Text.Json.Serialization", [
|
|
260
|
-
"System.Text.Json.Serialization",
|
|
261
|
-
]);
|
|
262
|
-
context.file.imports.set("System.Threading.Tasks", ["System.Threading.Tasks"]);
|
|
263
|
-
context.file.imports.set("Microsoft.AspNetCore.Mvc", ["Microsoft.AspNetCore.Mvc"]);
|
|
264
|
-
context.file.imports.set(modelNamespace, [modelNamespace]);
|
|
265
|
-
return context;
|
|
266
|
-
}
|
|
267
|
-
interfaceDeclarationOperations(iface) {
|
|
268
|
-
// add in operations
|
|
269
|
-
const builder = new StringBuilder();
|
|
270
|
-
const metadata = new HttpMetadata();
|
|
271
|
-
const context = this.emitter.getContext();
|
|
272
|
-
for (const [name, operation] of iface.operations) {
|
|
273
|
-
const doc = getDoc(this.emitter.getProgram(), operation);
|
|
274
|
-
const returnTypes = [];
|
|
275
|
-
const [httpOp, _] = getHttpOperation(this.emitter.getProgram(), operation);
|
|
276
|
-
for (const response of httpOp.responses.filter((r) => !isErrorModel(this.emitter.getProgram(), r.type))) {
|
|
277
|
-
returnTypes.push(metadata.resolveLogicalResponseType(this.emitter.getProgram(), response));
|
|
278
|
-
}
|
|
279
|
-
const returnInfo = coalesceTypes(this.emitter.getProgram(), returnTypes, context.namespace);
|
|
280
|
-
const returnType = returnInfo?.type || UnknownType;
|
|
281
|
-
const opName = ensureCSharpIdentifier(this.emitter.getProgram(), operation, name, NameCasingType.Method);
|
|
282
|
-
const opDecl = this.emitter.result.declaration(opName, code `${doc ? `${formatComment(doc)}\n` : ""}${returnType.name === "void" ? "Task" : `Task<${returnType.getTypeReference(context.scope)}>`} ${opName}Async( ${this.#emitInterfaceOperationParameters(operation, opName, "")});`);
|
|
283
|
-
builder.push(code `${opDecl.value}\n`);
|
|
284
|
-
this.emitter.emitInterfaceOperation(operation);
|
|
285
|
-
}
|
|
286
|
-
return builder.reduce();
|
|
287
|
-
}
|
|
288
|
-
interfaceOperationDeclarationContext(operation) {
|
|
289
|
-
const resource = getResourceOperation(this.emitter.getProgram(), operation);
|
|
290
|
-
const controllerName = operation.interface !== undefined
|
|
291
|
-
? operation.interface.name
|
|
292
|
-
: resource === undefined
|
|
293
|
-
? NoResourceContext
|
|
294
|
-
: resource.resourceType.name;
|
|
295
|
-
return this.#createOrGetResourceContext(controllerName, operation, resource?.resourceType);
|
|
296
|
-
}
|
|
297
|
-
interfaceOperationDeclaration(operation, name) {
|
|
298
|
-
const operationName = ensureCSharpIdentifier(this.emitter.getProgram(), operation, name, NameCasingType.Method);
|
|
299
|
-
const doc = getDoc(this.emitter.getProgram(), operation);
|
|
300
|
-
const [httpOperation, _] = getHttpOperation(this.emitter.getProgram(), operation);
|
|
301
|
-
const declParams = this.#emitHttpOperationParameters(httpOperation);
|
|
302
|
-
const responseInfo = this.#getOperationResponse(httpOperation);
|
|
303
|
-
let status = "200";
|
|
304
|
-
let response = new CSharpType({
|
|
305
|
-
name: "void",
|
|
306
|
-
namespace: "System",
|
|
307
|
-
isBuiltIn: true,
|
|
308
|
-
isValueType: false,
|
|
309
|
-
});
|
|
310
|
-
if (responseInfo !== undefined) {
|
|
311
|
-
[status, response] = responseInfo;
|
|
312
|
-
}
|
|
313
|
-
const hasResponseValue = response.name !== "void";
|
|
314
|
-
const resultString = `${status === "204" ? "NoContent" : "Ok"}`;
|
|
315
|
-
return this.emitter.result.declaration(operation.name, code `
|
|
316
|
-
${doc ? `${formatComment(doc)}` : ""}
|
|
317
|
-
[${getOperationVerbDecorator(httpOperation)}]
|
|
318
|
-
[Route("${httpOperation.path}")]
|
|
319
|
-
${this.emitter.emitOperationReturnType(operation)}
|
|
320
|
-
public virtual async Task<IActionResult> ${operationName}(${declParams})
|
|
321
|
-
{
|
|
322
|
-
${hasResponseValue
|
|
323
|
-
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${this.#emitOperationCallParameters(httpOperation)});
|
|
324
|
-
return ${resultString}(result);`
|
|
325
|
-
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${this.#emitOperationCallParameters(httpOperation)});
|
|
326
|
-
return ${resultString}();`}
|
|
327
|
-
}`);
|
|
328
|
-
}
|
|
329
|
-
operationDeclarationContext(operation, name) {
|
|
330
|
-
const resource = getResourceOperation(this.emitter.getProgram(), operation);
|
|
331
|
-
const controllerName = (operation.interface?.name ?? resource === undefined)
|
|
332
|
-
? NoResourceContext
|
|
333
|
-
: resource.resourceType.name;
|
|
334
|
-
return this.#createOrGetResourceContext(controllerName, operation, resource?.resourceType);
|
|
335
|
-
}
|
|
336
|
-
operationDeclaration(operation, name) {
|
|
337
|
-
const operationName = ensureCSharpIdentifier(this.emitter.getProgram(), operation, name, NameCasingType.Method);
|
|
338
|
-
const doc = getDoc(this.emitter.getProgram(), operation);
|
|
339
|
-
const [httpOperation, _] = getHttpOperation(this.emitter.getProgram(), operation);
|
|
340
|
-
const declParams = this.#emitHttpOperationParameters(httpOperation);
|
|
341
|
-
const responseInfo = this.#getOperationResponse(httpOperation);
|
|
342
|
-
let status = "200";
|
|
343
|
-
let response = new CSharpType({
|
|
344
|
-
name: "void",
|
|
345
|
-
namespace: "System",
|
|
346
|
-
isBuiltIn: true,
|
|
347
|
-
isValueType: false,
|
|
348
|
-
});
|
|
349
|
-
if (responseInfo !== undefined) {
|
|
350
|
-
[status, response] = responseInfo;
|
|
351
|
-
}
|
|
352
|
-
const hasResponseValue = response.name !== "void";
|
|
353
|
-
const resultString = `${status === "204" ? "NoContent" : "Ok"}`;
|
|
354
|
-
return this.emitter.result.declaration(operation.name, code `
|
|
355
|
-
${doc ? `${formatComment(doc)}` : ""}
|
|
356
|
-
[${getOperationVerbDecorator(httpOperation)}]
|
|
357
|
-
[Route("${httpOperation.path}")]
|
|
358
|
-
${this.emitter.emitOperationReturnType(operation)}
|
|
359
|
-
public virtual async Task<IActionResult> ${operationName}(${declParams})
|
|
360
|
-
{
|
|
361
|
-
${hasResponseValue
|
|
362
|
-
? `var result = await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${this.#emitOperationCallParameters(httpOperation)});
|
|
363
|
-
return ${resultString}(result);`
|
|
364
|
-
: `await ${this.emitter.getContext().resourceName}Impl.${operationName}Async(${this.#emitOperationCallParameters(httpOperation)});
|
|
365
|
-
return ${resultString}();`}
|
|
366
|
-
}`);
|
|
367
|
-
}
|
|
368
|
-
operationReturnType(operation, returnType) {
|
|
369
|
-
const [httpOperation, _] = getHttpOperation(this.emitter.getProgram(), operation);
|
|
370
|
-
return this.#emitOperationResponses(httpOperation);
|
|
371
|
-
}
|
|
372
|
-
#getOperationResponse(operation) {
|
|
373
|
-
const validResponses = operation.responses.filter((r) => !isErrorModel(this.emitter.getProgram(), r.type) &&
|
|
374
|
-
getCSharpStatusCode(r.statusCodes) !== undefined);
|
|
375
|
-
if (validResponses.length < 1)
|
|
376
|
-
return undefined;
|
|
377
|
-
const response = validResponses[0];
|
|
378
|
-
const statusCode = getCSharpStatusCode(response.statusCodes);
|
|
379
|
-
if (statusCode === undefined)
|
|
380
|
-
return undefined;
|
|
381
|
-
const responseType = new HttpMetadata().resolveLogicalResponseType(this.emitter.getProgram(), response);
|
|
382
|
-
const context = this.emitter.getContext();
|
|
383
|
-
const result = getCSharpType(this.emitter.getProgram(), responseType, context.namespace);
|
|
384
|
-
const resultType = result?.type || UnknownType;
|
|
385
|
-
return [statusCode, resultType];
|
|
386
|
-
}
|
|
387
|
-
#emitOperationResponses(operation) {
|
|
388
|
-
const builder = new StringBuilder();
|
|
389
|
-
let i = 0;
|
|
390
|
-
const validResponses = operation.responses.filter((r) => !isErrorModel(this.emitter.getProgram(), r.type) &&
|
|
391
|
-
getCSharpStatusCode(r.statusCodes) !== undefined);
|
|
392
|
-
for (const response of validResponses) {
|
|
393
|
-
i++;
|
|
394
|
-
builder.push(code `${this.#emitOperationResponseDecorator(response)}`);
|
|
395
|
-
if (i < validResponses.length) {
|
|
396
|
-
builder.pushLiteralSegment("\n");
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
for (const response of operation.responses) {
|
|
400
|
-
this.emitter.emitType(response.type);
|
|
401
|
-
}
|
|
402
|
-
return builder.reduce();
|
|
403
|
-
}
|
|
404
|
-
#emitOperationResponseDecorator(response) {
|
|
405
|
-
const responseType = new HttpMetadata().resolveLogicalResponseType(this.emitter.getProgram(), response);
|
|
406
|
-
return this.emitter.result.rawCode(code `[ProducesResponseType((int)${getCSharpStatusCode(response.statusCodes)}, Type = typeof(${this.#emitResponseType(responseType)}))]`);
|
|
407
|
-
}
|
|
408
|
-
#emitResponseType(type) {
|
|
409
|
-
const context = this.emitter.getContext();
|
|
410
|
-
const result = getCSharpType(this.emitter.getProgram(), type, context.namespace);
|
|
411
|
-
const resultType = result?.type || UnknownType;
|
|
412
|
-
return resultType.getTypeReference(context.scope);
|
|
413
|
-
}
|
|
414
|
-
#emitInterfaceOperationParameters(operation, operationName, resourceName) {
|
|
415
|
-
const signature = new StringBuilder();
|
|
416
|
-
const requiredParams = [];
|
|
417
|
-
const optionalParams = [];
|
|
418
|
-
let totalParams = 0;
|
|
419
|
-
for (const [_, parameter] of operation.parameters.properties) {
|
|
420
|
-
if (parameter.optional)
|
|
421
|
-
optionalParams.push(parameter);
|
|
422
|
-
else
|
|
423
|
-
requiredParams.push(parameter);
|
|
424
|
-
totalParams++;
|
|
425
|
-
}
|
|
426
|
-
let i = 1;
|
|
427
|
-
for (const requiredParam of requiredParams) {
|
|
428
|
-
signature.push(code `${this.emitter.emitTypeReference(requiredParam.type)} ${ensureCSharpIdentifier(this.emitter.getProgram(), requiredParam, requiredParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`);
|
|
429
|
-
}
|
|
430
|
-
for (const optionalParam of optionalParams) {
|
|
431
|
-
signature.push(code `${this.emitter.emitTypeReference(optionalParam.type)}? ${ensureCSharpIdentifier(this.emitter.getProgram(), optionalParam, optionalParam.name, NameCasingType.Parameter)}${i++ < totalParams ? ", " : ""}`);
|
|
432
|
-
}
|
|
433
|
-
return signature.reduce();
|
|
434
|
-
}
|
|
435
|
-
#emitHttpOperationParameters(operation) {
|
|
436
|
-
const signature = new StringBuilder();
|
|
437
|
-
const bodyParam = operation.parameters.body;
|
|
438
|
-
let i = 0;
|
|
439
|
-
//const pathParameters = operation.parameters.parameters.filter((p) => p.type === "path");
|
|
440
|
-
for (const parameter of operation.parameters.parameters) {
|
|
441
|
-
i++;
|
|
442
|
-
if (parameter.param.type.kind !== "Intrinsic" || parameter.param.type.name !== "never") {
|
|
443
|
-
signature.push(code `${this.#emitOperationSignatureParameter(operation, parameter)}${i < operation.parameters.parameters.length || bodyParam !== undefined ? ", " : ""}`);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (bodyParam !== undefined) {
|
|
447
|
-
signature.push(code `${this.emitter.emitTypeReference(this.#metaInfo.getEffectivePayloadType(bodyParam.type, Visibility.Create & Visibility.Update))} body`);
|
|
448
|
-
}
|
|
449
|
-
return signature.reduce();
|
|
450
|
-
}
|
|
451
|
-
unionDeclaration(union, name) {
|
|
452
|
-
const baseType = this.#coalesceUnionTypes(union);
|
|
453
|
-
if (baseType.isBuiltIn && baseType.name === "string") {
|
|
454
|
-
const program = this.emitter.getProgram();
|
|
455
|
-
const unionName = ensureCSharpIdentifier(program, union, name);
|
|
456
|
-
const namespace = this.emitter.getContext().namespace;
|
|
457
|
-
const doc = getDoc(this.emitter.getProgram(), union);
|
|
458
|
-
const attributes = getModelAttributes(program, union, unionName);
|
|
459
|
-
this.#metadateMap.set(union, new CSharpType({ name: unionName, namespace: namespace }));
|
|
460
|
-
return this.emitter.result.declaration(unionName, code `${this.#licenseHeader}
|
|
461
|
-
// <auto-generated />
|
|
462
|
-
|
|
463
|
-
${this.#emitUsings()}
|
|
464
|
-
|
|
465
|
-
namespace ${namespace}
|
|
466
|
-
{
|
|
467
|
-
|
|
468
|
-
${doc ? `${formatComment(doc)}` : ""}
|
|
469
|
-
${attributes.map((attribute) => attribute.getApplicationString(this.emitter.getContext().scope)).join("\n")}
|
|
470
|
-
public enum ${unionName}
|
|
471
|
-
{
|
|
472
|
-
${this.emitter.emitUnionVariants(union)};
|
|
473
|
-
}
|
|
474
|
-
} `);
|
|
475
|
-
}
|
|
476
|
-
return this.emitter.result.rawCode(code `${baseType.getTypeReference()}`);
|
|
477
|
-
}
|
|
478
|
-
unionDeclarationContext(union) {
|
|
479
|
-
const baseType = this.#coalesceUnionTypes(union);
|
|
480
|
-
if (baseType.isBuiltIn && baseType.name === "string") {
|
|
481
|
-
const unionName = ensureCSharpIdentifier(this.emitter.getProgram(), union, union.name || "Union");
|
|
482
|
-
const unionFile = this.emitter.createSourceFile(`models/${unionName}.cs`);
|
|
483
|
-
unionFile.meta[this.#sourceTypeKey] = CSharpSourceType.Model;
|
|
484
|
-
const unionNamespace = `${this.#getOrSetBaseNamespace(union)}.Models`;
|
|
485
|
-
return {
|
|
486
|
-
namespace: unionNamespace,
|
|
487
|
-
file: unionFile,
|
|
488
|
-
scope: unionFile.globalScope,
|
|
489
|
-
};
|
|
490
|
-
}
|
|
491
|
-
else {
|
|
492
|
-
return this.emitter.getContext();
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
|
-
unionInstantiation(union, name) {
|
|
496
|
-
return super.unionInstantiation(union, name);
|
|
497
|
-
}
|
|
498
|
-
unionInstantiationContext(union, name) {
|
|
499
|
-
return super.unionInstantiationContext(union, name);
|
|
500
|
-
}
|
|
501
|
-
unionVariants(union) {
|
|
502
|
-
const result = new StringBuilder();
|
|
503
|
-
let i = 0;
|
|
504
|
-
for (const [name, variant] of union.variants) {
|
|
505
|
-
i++;
|
|
506
|
-
if (variant.type.kind === "String") {
|
|
507
|
-
const nameHint = name || variant.type.value;
|
|
508
|
-
const memberName = ensureCSharpIdentifier(this.emitter.getProgram(), variant, nameHint);
|
|
509
|
-
this.#metadateMap.set(variant, { name: memberName });
|
|
510
|
-
result.push(code `${ensureCSharpIdentifier(this.emitter.getProgram(), variant, nameHint)} = "${variant.type.value}"`);
|
|
511
|
-
if (i < union.variants.size)
|
|
512
|
-
result.pushLiteralSegment(", ");
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
return this.emitter.result.rawCode(result.reduce());
|
|
516
|
-
}
|
|
517
|
-
unionVariantContext(union) {
|
|
518
|
-
return super.unionVariantContext(union);
|
|
519
|
-
}
|
|
520
|
-
#emitOperationSignatureParameter(operation, httpParam) {
|
|
521
|
-
const name = httpParam.param.name;
|
|
522
|
-
const parameter = httpParam.param;
|
|
523
|
-
const emittedName = ensureCSharpIdentifier(this.emitter.getProgram(), parameter, name, NameCasingType.Parameter);
|
|
524
|
-
const [emittedType, emittedDefault] = this.#findPropertyType(parameter);
|
|
525
|
-
// eslint-disable-next-line deprecation/deprecation
|
|
526
|
-
const defaultValue = parameter.default
|
|
527
|
-
? // eslint-disable-next-line deprecation/deprecation
|
|
528
|
-
code `${this.emitter.emitType(parameter.default)}`
|
|
529
|
-
: emittedDefault;
|
|
530
|
-
return this.emitter.result.rawCode(code `${httpParam.type !== "path" ? this.#emitParameterAttribute(httpParam) : ""}${emittedType} ${emittedName}${defaultValue === undefined ? "" : ` = ${defaultValue}`}`);
|
|
531
|
-
}
|
|
532
|
-
#emitOperationCallParameters(operation) {
|
|
533
|
-
const signature = new StringBuilder();
|
|
534
|
-
const bodyParam = operation.parameters.body;
|
|
535
|
-
let i = 0;
|
|
536
|
-
//const pathParameters = operation.parameters.parameters.filter((p) => p.type === "path");
|
|
537
|
-
for (const parameter of operation.parameters.parameters) {
|
|
538
|
-
i++;
|
|
539
|
-
if (!isNeverType(parameter.param.type) &&
|
|
540
|
-
!isNullType(parameter.param.type) &&
|
|
541
|
-
!isVoidType(parameter.param.type)) {
|
|
542
|
-
signature.push(code `${this.#emitOperationCallParameter(operation, parameter)}${i < operation.parameters.parameters.length || bodyParam !== undefined ? ", " : ""}`);
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
if (bodyParam !== undefined) {
|
|
546
|
-
signature.push(code `body`);
|
|
547
|
-
}
|
|
548
|
-
return signature.reduce();
|
|
549
|
-
}
|
|
550
|
-
#emitOperationCallParameter(operation, httpParam) {
|
|
551
|
-
const name = httpParam.param.name;
|
|
552
|
-
const parameter = httpParam.param;
|
|
553
|
-
const emittedName = ensureCSharpIdentifier(this.emitter.getProgram(), parameter, name, NameCasingType.Parameter);
|
|
554
|
-
return this.emitter.result.rawCode(code `${emittedName}`);
|
|
555
|
-
}
|
|
556
|
-
#emitParameterAttribute(parameter) {
|
|
557
|
-
switch (parameter.type) {
|
|
558
|
-
case "header":
|
|
559
|
-
return code `[FromHeader(Name="${parameter.name}")] `;
|
|
560
|
-
case "query":
|
|
561
|
-
return code `[FromQuery(Name="${parameter.name}")] `;
|
|
562
|
-
default:
|
|
563
|
-
return "";
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
#createModelContext(namespace, file) {
|
|
567
|
-
const context = {
|
|
568
|
-
namespace: namespace,
|
|
569
|
-
file: file,
|
|
570
|
-
scope: file.globalScope,
|
|
571
|
-
};
|
|
572
|
-
context.file.imports.set("System", ["System"]);
|
|
573
|
-
context.file.imports.set("System.Text.Json", ["System.Text.Json"]);
|
|
574
|
-
context.file.imports.set("System.Text.Json.Serialization", [
|
|
575
|
-
"System.Text.Json.Serialization",
|
|
576
|
-
]);
|
|
577
|
-
return context;
|
|
578
|
-
}
|
|
579
|
-
#createOrGetResourceContext(name, operation, resource) {
|
|
580
|
-
let context = controllers.get(name);
|
|
581
|
-
if (context !== undefined)
|
|
582
|
-
return context;
|
|
583
|
-
const sourceFile = this.emitter.createSourceFile(`controllers/${name}ControllerBase.cs`);
|
|
584
|
-
const namespace = this.#getOrSetBaseNamespace(operation);
|
|
585
|
-
const modelNamespace = `${namespace}.Models`;
|
|
586
|
-
sourceFile.meta[this.#sourceTypeKey] = CSharpSourceType.Controller;
|
|
587
|
-
sourceFile.meta["resourceName"] = name;
|
|
588
|
-
sourceFile.meta["resource"] = `${name}Controller`;
|
|
589
|
-
sourceFile.meta["namespace"] = namespace;
|
|
590
|
-
context = {
|
|
591
|
-
namespace: sourceFile.meta["namespace"],
|
|
592
|
-
file: sourceFile,
|
|
593
|
-
resourceName: name,
|
|
594
|
-
scope: sourceFile.globalScope,
|
|
595
|
-
resourceType: resource,
|
|
596
|
-
};
|
|
597
|
-
context.file.imports.set("System", ["System"]);
|
|
598
|
-
context.file.imports.set("System.Net", ["System.Net"]);
|
|
599
|
-
context.file.imports.set("System.Threading.Tasks", ["System.Threading.Tasks"]);
|
|
600
|
-
context.file.imports.set("System.Text.Json", ["System.Text.Json"]);
|
|
601
|
-
context.file.imports.set("System.Text.Json.Serialization", [
|
|
602
|
-
"System.Text.Json.Serialization",
|
|
603
|
-
]);
|
|
604
|
-
context.file.imports.set("Microsoft.AspNetCore.Mvc", ["Microsoft.AspNetCore.Mvc"]);
|
|
605
|
-
context.file.imports.set(modelNamespace, [modelNamespace]);
|
|
606
|
-
context.file.imports.set(namespace, [namespace]);
|
|
607
|
-
controllers.set(name, context);
|
|
608
|
-
return context;
|
|
609
|
-
}
|
|
610
|
-
#getNamespaceFullName(namespace) {
|
|
611
|
-
return namespace
|
|
612
|
-
? ensureCSharpIdentifier(this.emitter.getProgram(), namespace, getNamespaceFullName(namespace))
|
|
613
|
-
: "TypeSpec";
|
|
614
|
-
}
|
|
615
|
-
reference(targetDeclaration, pathUp, pathDown, commonScope) {
|
|
616
|
-
//if (targetDeclaration.name) return targetDeclaration.name;
|
|
617
|
-
return super.reference(targetDeclaration, pathUp, pathDown, commonScope);
|
|
618
|
-
}
|
|
619
|
-
scalarInstantiation(scalar, name) {
|
|
620
|
-
const scalarType = getCSharpTypeForScalar(this.emitter.getProgram(), scalar);
|
|
621
|
-
return scalarType.getTypeReference(this.emitter.getContext().scope);
|
|
622
|
-
}
|
|
623
|
-
scalarDeclaration(scalar, name) {
|
|
624
|
-
const foo = new Placeholder();
|
|
625
|
-
foo.setValue;
|
|
626
|
-
const scalarType = getCSharpTypeForScalar(this.emitter.getProgram(), scalar);
|
|
627
|
-
return scalarType.getTypeReference(this.emitter.getContext().scope);
|
|
628
|
-
}
|
|
629
|
-
sourceFile(sourceFile) {
|
|
630
|
-
for (const libFile of this.#libraryFiles) {
|
|
631
|
-
if (sourceFile === libFile.source)
|
|
632
|
-
return libFile.emitted;
|
|
633
|
-
}
|
|
634
|
-
const emittedSourceFile = {
|
|
635
|
-
path: sourceFile.path,
|
|
636
|
-
contents: "",
|
|
637
|
-
};
|
|
638
|
-
switch (sourceFile.meta[this.#sourceTypeKey]) {
|
|
639
|
-
case CSharpSourceType.Controller:
|
|
640
|
-
emittedSourceFile.contents = this.#emitControllerContents(sourceFile);
|
|
641
|
-
break;
|
|
642
|
-
default:
|
|
643
|
-
emittedSourceFile.contents = this.#emitCodeContents(sourceFile);
|
|
644
|
-
break;
|
|
645
|
-
}
|
|
646
|
-
return emittedSourceFile;
|
|
647
|
-
}
|
|
648
|
-
#emitCodeContents(file) {
|
|
649
|
-
const contents = new StringBuilder();
|
|
650
|
-
for (const decl of file.globalScope.declarations) {
|
|
651
|
-
contents.push(decl.value);
|
|
652
|
-
}
|
|
653
|
-
return contents.segments.join("\n") + "\n";
|
|
654
|
-
}
|
|
655
|
-
#emitControllerContents(file) {
|
|
656
|
-
const namespace = file.meta.namespace;
|
|
657
|
-
const contents = new StringBuilder();
|
|
658
|
-
contents.push(`${this.#licenseHeader}\n`);
|
|
659
|
-
contents.push("// <auto-generated />\n\n");
|
|
660
|
-
contents.push(code `${this.#emitUsings(file)}\n`);
|
|
661
|
-
contents.push("\n");
|
|
662
|
-
contents.push(`namespace ${namespace}.Controllers\n`);
|
|
663
|
-
contents.push("{\n");
|
|
664
|
-
contents.push("[ApiController]\n");
|
|
665
|
-
contents.push(`public abstract partial class ${file.meta["resource"]}Base: ControllerBase\n`);
|
|
666
|
-
contents.push("{\n");
|
|
667
|
-
contents.push("\n");
|
|
668
|
-
contents.push(code `internal abstract I${file.meta.resourceName} ${file.meta.resourceName}Impl { get;}\n`);
|
|
669
|
-
for (const decl of file.globalScope.declarations) {
|
|
670
|
-
contents.push(decl.value + "\n");
|
|
671
|
-
}
|
|
672
|
-
contents.push("\n}");
|
|
673
|
-
contents.push("\n}");
|
|
674
|
-
return contents.segments.join("\n") + "\n";
|
|
675
|
-
}
|
|
676
|
-
stringLiteral(string) {
|
|
677
|
-
return this.emitter.result.rawCode(code `"${string.value}"`);
|
|
678
|
-
}
|
|
679
|
-
tupleLiteral(tuple) {
|
|
680
|
-
return this.emitter.result.rawCode(code `{
|
|
681
|
-
${this.emitter.emitTupleLiteralValues(tuple)}
|
|
682
|
-
}`);
|
|
683
|
-
}
|
|
684
|
-
tupleLiteralValues(tuple) {
|
|
685
|
-
const result = new StringBuilder();
|
|
686
|
-
for (const tupleValue of tuple.values) {
|
|
687
|
-
result.push(code `${this.emitter.emitType(tupleValue)}`);
|
|
688
|
-
}
|
|
689
|
-
return this.emitter.result.rawCode(result.segments.join(",\n"));
|
|
690
|
-
}
|
|
691
|
-
createModelScope(baseScope, namespace) {
|
|
692
|
-
let current = baseScope;
|
|
693
|
-
for (const part of namespace.split(".")) {
|
|
694
|
-
current = this.emitter.createScope({}, getCSharpIdentifier(part), current);
|
|
695
|
-
}
|
|
696
|
-
return current;
|
|
697
|
-
}
|
|
698
|
-
#getTemplateParameters(model) {
|
|
699
|
-
if (!model.templateMapper)
|
|
700
|
-
return "";
|
|
701
|
-
let i = 0;
|
|
702
|
-
const params = new StringBuilder();
|
|
703
|
-
const args = model.templateMapper.args
|
|
704
|
-
.flatMap((parm) => parm)
|
|
705
|
-
.filter((arg) => arg !== null && isKnownReferenceType(this.emitter.getProgram(), arg));
|
|
706
|
-
for (const parameter of args) {
|
|
707
|
-
i++;
|
|
708
|
-
params.push(code `${this.emitter.emitTypeReference(parameter)}`);
|
|
709
|
-
if (i < args.length) {
|
|
710
|
-
params.pushLiteralSegment(",");
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
if (params.segments.length > 0)
|
|
714
|
-
return params.reduce();
|
|
715
|
-
return "";
|
|
716
|
-
}
|
|
717
|
-
#coalesceUnionTypes(union) {
|
|
718
|
-
const defaultValue = new CSharpType({
|
|
719
|
-
name: "object",
|
|
720
|
-
namespace: "System",
|
|
721
|
-
isValueType: false,
|
|
722
|
-
});
|
|
723
|
-
let current = undefined;
|
|
724
|
-
for (const [_, variant] of union.variants.entries()) {
|
|
725
|
-
let candidate;
|
|
726
|
-
switch (variant.type.kind) {
|
|
727
|
-
case "Boolean":
|
|
728
|
-
candidate = new CSharpType({ name: "bool", namespace: "System", isValueType: true });
|
|
729
|
-
break;
|
|
730
|
-
case "String":
|
|
731
|
-
candidate = new CSharpType({ name: "string", namespace: "System", isValueType: false });
|
|
732
|
-
break;
|
|
733
|
-
case "Union":
|
|
734
|
-
candidate = this.#coalesceUnionTypes(variant.type);
|
|
735
|
-
break;
|
|
736
|
-
case "Scalar":
|
|
737
|
-
candidate = getCSharpTypeForScalar(this.emitter.getProgram(), variant.type);
|
|
738
|
-
break;
|
|
739
|
-
default:
|
|
740
|
-
return defaultValue;
|
|
741
|
-
}
|
|
742
|
-
current = current ?? candidate;
|
|
743
|
-
if (current === undefined || !candidate.equals(current))
|
|
744
|
-
return defaultValue;
|
|
745
|
-
}
|
|
746
|
-
return current ?? defaultValue;
|
|
747
|
-
}
|
|
748
|
-
writeOutput(sourceFiles) {
|
|
749
|
-
for (const source of this.#libraryFiles) {
|
|
750
|
-
sourceFiles.push(source.source);
|
|
751
|
-
}
|
|
752
|
-
const emittedSourceFiles = [];
|
|
753
|
-
for (const source of sourceFiles) {
|
|
754
|
-
switch (this.#emitterOutputType) {
|
|
755
|
-
case "models":
|
|
756
|
-
{
|
|
757
|
-
switch (source.meta[this.#sourceTypeKey]) {
|
|
758
|
-
case CSharpSourceType.Controller:
|
|
759
|
-
// do nothing
|
|
760
|
-
break;
|
|
761
|
-
default:
|
|
762
|
-
emittedSourceFiles.push(source);
|
|
763
|
-
break;
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
break;
|
|
767
|
-
default:
|
|
768
|
-
emittedSourceFiles.push(source);
|
|
769
|
-
break;
|
|
770
|
-
}
|
|
771
|
-
}
|
|
772
|
-
return super.writeOutput(emittedSourceFiles);
|
|
773
|
-
}
|
|
774
|
-
#getOrSetBaseNamespace(type) {
|
|
775
|
-
if (this.#baseNamespace === undefined) {
|
|
776
|
-
if (type.namespace !== undefined) {
|
|
777
|
-
this.#baseNamespace = `${type.namespace
|
|
778
|
-
? ensureCSharpIdentifier(this.emitter.getProgram(), type.namespace, getNamespaceFullName(type.namespace))
|
|
779
|
-
: "TypeSpec"}.Service`;
|
|
780
|
-
}
|
|
781
|
-
else {
|
|
782
|
-
this.#baseNamespace = "TypeSpec.Service";
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
return this.#baseNamespace;
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
function processNameSpace(program, target) {
|
|
789
|
-
const service = getService(program, target);
|
|
790
|
-
if (service) {
|
|
791
|
-
for (const [_, model] of target.models) {
|
|
792
|
-
emitter.emitType(model);
|
|
793
|
-
}
|
|
794
|
-
for (const [_, en] of target.enums) {
|
|
795
|
-
emitter.emitType(en);
|
|
796
|
-
}
|
|
797
|
-
for (const [_, sc] of target.scalars) {
|
|
798
|
-
emitter.emitType(sc);
|
|
799
|
-
}
|
|
800
|
-
for (const [_, iface] of target.interfaces) {
|
|
801
|
-
emitter.emitType(iface);
|
|
802
|
-
}
|
|
803
|
-
for (const [_, op] of target.operations) {
|
|
804
|
-
emitter.emitType(op);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
else {
|
|
808
|
-
for (const [_, sub] of target.namespaces) {
|
|
809
|
-
processNameSpace(program, sub);
|
|
810
|
-
}
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
const emitter = createAssetEmitter(context.program, CSharpCodeEmitter, context);
|
|
814
|
-
const ns = context.program.checker.getGlobalNamespaceType();
|
|
815
|
-
const options = emitter.getOptions();
|
|
816
|
-
processNameSpace(context.program, ns);
|
|
817
|
-
await ensureCleanDirectory(context.program, options.emitterOutputDir);
|
|
818
|
-
await emitter.writeOutput();
|
|
819
|
-
if (options["skip-format"] === undefined || options["skip-format"] === false) {
|
|
820
|
-
await execFile("dotnet", [
|
|
821
|
-
"format",
|
|
822
|
-
"whitespace",
|
|
823
|
-
emitter.getOptions().emitterOutputDir,
|
|
824
|
-
"--include-generated",
|
|
825
|
-
"--folder",
|
|
826
|
-
]);
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
//# sourceMappingURL=service.js.map
|