@azure-tools/typespec-rust 0.35.0 → 0.37.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/dist/src/codegen/clients.d.ts +4 -4
- package/dist/src/codegen/clients.d.ts.map +1 -1
- package/dist/src/codegen/clients.js +177 -69
- package/dist/src/codegen/clients.js.map +1 -1
- package/dist/src/codegen/codeGenerator.d.ts +6 -0
- package/dist/src/codegen/codeGenerator.d.ts.map +1 -1
- package/dist/src/codegen/codeGenerator.js +112 -32
- package/dist/src/codegen/codeGenerator.js.map +1 -1
- package/dist/src/codegen/context.d.ts +10 -0
- package/dist/src/codegen/context.d.ts.map +1 -1
- package/dist/src/codegen/context.js +58 -17
- package/dist/src/codegen/context.js.map +1 -1
- package/dist/src/codegen/docTests.d.ts +1 -2
- package/dist/src/codegen/docTests.d.ts.map +1 -1
- package/dist/src/codegen/docTests.js +13 -4
- package/dist/src/codegen/docTests.js.map +1 -1
- package/dist/src/codegen/enums.d.ts +4 -4
- package/dist/src/codegen/enums.d.ts.map +1 -1
- package/dist/src/codegen/enums.js +21 -21
- package/dist/src/codegen/enums.js.map +1 -1
- package/dist/src/codegen/headerTraits.d.ts +2 -2
- package/dist/src/codegen/headerTraits.d.ts.map +1 -1
- package/dist/src/codegen/headerTraits.js +17 -9
- package/dist/src/codegen/headerTraits.js.map +1 -1
- package/dist/src/codegen/helpers.d.ts +12 -3
- package/dist/src/codegen/helpers.d.ts.map +1 -1
- package/dist/src/codegen/helpers.js +19 -13
- package/dist/src/codegen/helpers.js.map +1 -1
- package/dist/src/codegen/lib.d.ts +3 -1
- package/dist/src/codegen/lib.d.ts.map +1 -1
- package/dist/src/codegen/lib.js +5 -1
- package/dist/src/codegen/lib.js.map +1 -1
- package/dist/src/codegen/mod.d.ts +9 -2
- package/dist/src/codegen/mod.d.ts.map +1 -1
- package/dist/src/codegen/mod.js +25 -9
- package/dist/src/codegen/mod.js.map +1 -1
- package/dist/src/codegen/models.d.ts +4 -4
- package/dist/src/codegen/models.d.ts.map +1 -1
- package/dist/src/codegen/models.js +76 -30
- package/dist/src/codegen/models.js.map +1 -1
- package/dist/src/codegen/unions.d.ts +4 -4
- package/dist/src/codegen/unions.d.ts.map +1 -1
- package/dist/src/codegen/unions.js +71 -45
- package/dist/src/codegen/unions.js.map +1 -1
- package/dist/src/codegen/use.d.ts +4 -2
- package/dist/src/codegen/use.d.ts.map +1 -1
- package/dist/src/codegen/use.js +22 -26
- package/dist/src/codegen/use.js.map +1 -1
- package/dist/src/codemodel/client.d.ts +31 -16
- package/dist/src/codemodel/client.d.ts.map +1 -1
- package/dist/src/codemodel/client.js +11 -7
- package/dist/src/codemodel/client.js.map +1 -1
- package/dist/src/codemodel/crate.d.ts +42 -12
- package/dist/src/codemodel/crate.d.ts.map +1 -1
- package/dist/src/codemodel/crate.js +21 -46
- package/dist/src/codemodel/crate.js.map +1 -1
- package/dist/src/codemodel/types.d.ts +60 -34
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +26 -10
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/emitter.d.ts.map +1 -1
- package/dist/src/emitter.js +39 -11
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/lib.d.ts +2 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +6 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +81 -13
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +711 -213
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/dist/src/utils/utils.d.ts +7 -4
- package/dist/src/utils/utils.d.ts.map +1 -1
- package/dist/src/utils/utils.js +25 -23
- package/dist/src/utils/utils.js.map +1 -1
- package/package.json +17 -17
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
3
|
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
4
|
*--------------------------------------------------------------------------------------------*/
|
|
5
|
-
// cspell: ignore addl responseheader subclients lropaging
|
|
5
|
+
// cspell: ignore addl clientgenerator requiredness resourcemanager responseheader subclients lropaging
|
|
6
6
|
import * as tsp from '@typespec/compiler';
|
|
7
|
+
import * as http from '@typespec/http';
|
|
7
8
|
import * as helpers from './helpers.js';
|
|
8
9
|
import * as naming from './naming.js';
|
|
9
10
|
import * as utils from '../utils/utils.js';
|
|
@@ -57,11 +58,32 @@ export class Adapter {
|
|
|
57
58
|
return new Adapter(ctx, context.options);
|
|
58
59
|
}
|
|
59
60
|
constructor(ctx, options) {
|
|
61
|
+
this.rootNamespace = '';
|
|
60
62
|
this.types = new Map();
|
|
61
63
|
this.clientMethodParams = new Map();
|
|
62
64
|
this.fieldsMap = new Map();
|
|
63
65
|
this.ctx = ctx;
|
|
64
66
|
this.options = options;
|
|
67
|
+
// this and adjacent code was taken from
|
|
68
|
+
// https://github.com/microsoft/typespec/blob/c0f464728f60fe3672204dd7f2907ea4c047dfcb/packages/http-client-python/emitter/src/utils.ts#L274
|
|
69
|
+
if (this.ctx.sdkPackage.clients.length > 0) {
|
|
70
|
+
this.rootNamespace = this.ctx.sdkPackage.clients[0].namespace;
|
|
71
|
+
}
|
|
72
|
+
else if (this.ctx.sdkPackage.models.length > 0) {
|
|
73
|
+
const result = this.ctx.sdkPackage.models
|
|
74
|
+
.map((model) => model.namespace)
|
|
75
|
+
.filter((namespace) => !isLibNamespace(namespace));
|
|
76
|
+
if (result.length > 0) {
|
|
77
|
+
result.sort();
|
|
78
|
+
this.rootNamespace = result[0];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
else if (this.ctx.sdkPackage.namespaces.length > 0) {
|
|
82
|
+
this.rootNamespace = this.ctx.sdkPackage.namespaces[0].fullName;
|
|
83
|
+
}
|
|
84
|
+
if (this.rootNamespace === '') {
|
|
85
|
+
throw new AdapterError('UnsupportedTsp', 'unable to determine root namespace');
|
|
86
|
+
}
|
|
65
87
|
let serviceType = 'data-plane';
|
|
66
88
|
if (this.ctx.arm === true) {
|
|
67
89
|
serviceType = 'azure-arm';
|
|
@@ -72,14 +94,6 @@ export class Adapter {
|
|
|
72
94
|
tcgcToCrate() {
|
|
73
95
|
this.adaptTypes();
|
|
74
96
|
this.adaptClients();
|
|
75
|
-
// marker models don't require serde so exclude them from the check
|
|
76
|
-
if (this.crate.enums.length > 0 || this.crate.models.some(e => e.kind === 'model')) {
|
|
77
|
-
this.crate.addDependency(new rust.CrateDependency('serde'));
|
|
78
|
-
}
|
|
79
|
-
if (this.crate.clients.length > 0 || this.crate.enums.length > 0 || this.crate.models.length > 0 || this.crate.unions.length > 0) {
|
|
80
|
-
this.crate.addDependency(new rust.CrateDependency('azure_core'));
|
|
81
|
-
}
|
|
82
|
-
this.crate.sortContent();
|
|
83
97
|
return this.crate;
|
|
84
98
|
}
|
|
85
99
|
/** converts tcgc docs to formatted rust.Docs */
|
|
@@ -95,15 +109,78 @@ export class Adapter {
|
|
|
95
109
|
description: doc,
|
|
96
110
|
};
|
|
97
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* adapts the specified namespace to a hierarchy of Rust modules
|
|
114
|
+
*
|
|
115
|
+
* @param namespace the namespace for which to return the package
|
|
116
|
+
* @returns the leaf module in the namespace
|
|
117
|
+
*/
|
|
118
|
+
adaptNamespace(namespace) {
|
|
119
|
+
// some example namespaces
|
|
120
|
+
// foo
|
|
121
|
+
// foo.bar
|
|
122
|
+
// foo.bar.baz
|
|
123
|
+
//
|
|
124
|
+
// if the namespace is empty or it's not under the root namespace or the
|
|
125
|
+
// root's parent namespace then it belongs to a core library, so redirect
|
|
126
|
+
// its content to the root namespace.
|
|
127
|
+
// when the root's parent IS a known library namespace (e.g. Azure.ResourceManager),
|
|
128
|
+
// sibling namespaces (like CommonTypes) also get redirected to root since they
|
|
129
|
+
// contain library types, not service types.
|
|
130
|
+
const nsLower = namespace.toLowerCase();
|
|
131
|
+
const rootLower = this.rootNamespace.toLowerCase();
|
|
132
|
+
const rootParentLower = rootLower.includes('.')
|
|
133
|
+
? rootLower.substring(0, rootLower.lastIndexOf('.'))
|
|
134
|
+
: '';
|
|
135
|
+
const isUnderRoot = nsLower === rootLower || nsLower.startsWith(rootLower + '.');
|
|
136
|
+
const isParent = rootParentLower !== '' && nsLower === rootParentLower;
|
|
137
|
+
const isSiblingOfRoot = rootParentLower !== '' && !isUnderRoot && nsLower.startsWith(rootParentLower + '.');
|
|
138
|
+
const isParentLibrary = LIB_NAMESPACE.includes(rootParentLower);
|
|
139
|
+
if (namespace === '' || isParent || (!isUnderRoot && (!isSiblingOfRoot || isParentLibrary))) {
|
|
140
|
+
namespace = this.rootNamespace;
|
|
141
|
+
}
|
|
142
|
+
// trim off the root namespace. if the result is the empty
|
|
143
|
+
// string it means the contents goes into the crate's root.
|
|
144
|
+
// also handle sibling namespaces: if the root namespace is Foo.Bar
|
|
145
|
+
// and we encounter Foo.Baz, treat Baz as a child module by trimming
|
|
146
|
+
// the shared parent (Foo) prefix.
|
|
147
|
+
if (namespace.toLowerCase().startsWith(this.rootNamespace.toLowerCase())) {
|
|
148
|
+
namespace = namespace.substring(this.rootNamespace.length + 1);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
const rootParent = this.rootNamespace.includes('.')
|
|
152
|
+
? this.rootNamespace.substring(0, this.rootNamespace.lastIndexOf('.'))
|
|
153
|
+
: '';
|
|
154
|
+
if (rootParent !== '' && namespace.toLowerCase().startsWith(rootParent.toLowerCase() + '.')) {
|
|
155
|
+
namespace = namespace.substring(rootParent.length + 1);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const namespaces = namespace.split('.').filter(Boolean);
|
|
159
|
+
let cur = this.crate;
|
|
160
|
+
for (const namespace of namespaces) {
|
|
161
|
+
const modName = utils.snakeCaseName(namespace);
|
|
162
|
+
let subMod = cur.subModules.find((each) => each.name === modName);
|
|
163
|
+
if (!subMod) {
|
|
164
|
+
subMod = new rust.SubModule(modName, cur);
|
|
165
|
+
cur.subModules.push(subMod);
|
|
166
|
+
}
|
|
167
|
+
cur = subMod;
|
|
168
|
+
}
|
|
169
|
+
return cur;
|
|
170
|
+
}
|
|
98
171
|
/** converts all tcgc types to their Rust type equivalent */
|
|
99
172
|
adaptTypes() {
|
|
100
|
-
|
|
173
|
+
let needsCoreAndSerde = false;
|
|
101
174
|
for (const sdkUnion of this.ctx.sdkPackage.unions.filter(u => u.kind === 'union')) {
|
|
102
175
|
if (!sdkUnion.discriminatedOptions) {
|
|
103
|
-
|
|
176
|
+
// getNonDiscriminatedUnion() self-registers the type into the module and caches it
|
|
177
|
+
this.getNonDiscriminatedUnion(sdkUnion);
|
|
178
|
+
needsCoreAndSerde = true;
|
|
179
|
+
continue;
|
|
104
180
|
}
|
|
105
181
|
const rustUnion = this.getDiscriminatedUnion(sdkUnion);
|
|
106
|
-
this.
|
|
182
|
+
this.adaptNamespace(sdkUnion.namespace).unions.push(rustUnion);
|
|
183
|
+
needsCoreAndSerde = true;
|
|
107
184
|
}
|
|
108
185
|
for (const sdkEnum of this.ctx.sdkPackage.enums) {
|
|
109
186
|
if ((sdkEnum.usage & tcgc.UsageFlags.ApiVersionEnum) === tcgc.UsageFlags.ApiVersionEnum) {
|
|
@@ -116,17 +193,49 @@ export class Adapter {
|
|
|
116
193
|
}
|
|
117
194
|
else {
|
|
118
195
|
const rustEnum = this.getEnum(sdkEnum);
|
|
119
|
-
this.
|
|
120
|
-
|
|
196
|
+
this.adaptNamespace(sdkEnum.namespace).enums.push(rustEnum);
|
|
197
|
+
needsCoreAndSerde = true;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
let terminalErrorModelNames = new Set();
|
|
201
|
+
if (this.options['emit-error-traits']) {
|
|
202
|
+
// Typespec would flag any model that is used in the error model with the Exception flag,
|
|
203
|
+
// even if that model is not directly used in any method, but is used as one of the terminal error type fields.
|
|
204
|
+
// We need to find these terminal error models, and ignore all others, because when we need to implement TryFrom trait,
|
|
205
|
+
// there is no need to implement it for every Exception model - we just need it for the terminal ones.
|
|
206
|
+
const getTerminalErrorModelNames = function (clients, visitedClientNames = new Set()) {
|
|
207
|
+
var _a;
|
|
208
|
+
const terminalErrorModelNames = new Set();
|
|
209
|
+
for (const client of clients) {
|
|
210
|
+
if (visitedClientNames.has(client.name)) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
visitedClientNames.add(client.name);
|
|
214
|
+
for (const errorModelName of client.methods.flatMap(mt => mt.operation.exceptions).filter(e => { var _a; return ((_a = e.type) === null || _a === void 0 ? void 0 : _a.kind) === 'model'; }).map(md => md.type.name)) {
|
|
215
|
+
terminalErrorModelNames.add(errorModelName);
|
|
216
|
+
}
|
|
217
|
+
for (const errorModelName of getTerminalErrorModelNames((_a = client.children) !== null && _a !== void 0 ? _a : [], visitedClientNames).values()) {
|
|
218
|
+
terminalErrorModelNames.add(errorModelName);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return terminalErrorModelNames;
|
|
222
|
+
};
|
|
223
|
+
terminalErrorModelNames = getTerminalErrorModelNames(this.ctx.sdkPackage.clients);
|
|
121
224
|
}
|
|
122
225
|
const processedTypes = new Set();
|
|
123
226
|
for (const model of this.ctx.sdkPackage.models) {
|
|
124
|
-
if ((model.usage & tcgc.UsageFlags.Input
|
|
227
|
+
if ((model.usage & (tcgc.UsageFlags.Input | tcgc.UsageFlags.Output | tcgc.UsageFlags.Spread | tcgc.UsageFlags.Exception)) === 0) {
|
|
125
228
|
// skip types without input and output usage. this will include core
|
|
126
229
|
// types unless they're explicitly referenced (e.g. a model property).
|
|
127
230
|
// we keep the models for spread params as we internally use them.
|
|
231
|
+
// We also emit exception (error) types.
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
// Skip the default Azure core error models.
|
|
235
|
+
if (tcgc.isAzureCoreModel(model)) {
|
|
128
236
|
continue;
|
|
129
237
|
}
|
|
238
|
+
needsCoreAndSerde = true;
|
|
130
239
|
// TODO: workaround for https://github.com/Azure/typespec-azure/issues/3614
|
|
131
240
|
if (processedTypes.has(model.name)) {
|
|
132
241
|
continue;
|
|
@@ -137,7 +246,7 @@ export class Adapter {
|
|
|
137
246
|
// END workaround
|
|
138
247
|
if (model.discriminatedSubtypes) {
|
|
139
248
|
const rustUnion = this.getDiscriminatedUnion(model);
|
|
140
|
-
this.
|
|
249
|
+
this.adaptNamespace(model.namespace).unions.push(rustUnion);
|
|
141
250
|
// we don't want to add the base type to the array
|
|
142
251
|
// of models to emit as we hijack its name to be used
|
|
143
252
|
// for the associated tagged enum type.
|
|
@@ -148,9 +257,16 @@ export class Adapter {
|
|
|
148
257
|
}
|
|
149
258
|
else {
|
|
150
259
|
const rustModel = this.getModel(model);
|
|
151
|
-
|
|
260
|
+
if (terminalErrorModelNames.has(model.name)) {
|
|
261
|
+
rustModel.flags |= rust.ModelFlags.Error;
|
|
262
|
+
}
|
|
263
|
+
this.adaptNamespace(model.namespace).models.push(rustModel);
|
|
152
264
|
}
|
|
153
265
|
}
|
|
266
|
+
if (needsCoreAndSerde) {
|
|
267
|
+
this.crate.addDependency(new rust.CrateDependency('azure_core'));
|
|
268
|
+
this.crate.addDependency(new rust.CrateDependency('serde'));
|
|
269
|
+
}
|
|
154
270
|
}
|
|
155
271
|
/**
|
|
156
272
|
* converts a tcgc enum to a Rust enum
|
|
@@ -188,7 +304,7 @@ export class Adapter {
|
|
|
188
304
|
default:
|
|
189
305
|
throw new AdapterError('UnsupportedTsp', `unsupported enum underlying type ${sdkEnum.valueType.kind}`, (_a = sdkEnum.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
190
306
|
}
|
|
191
|
-
rustEnum = new rust.Enum(enumName, adaptAccessFlags(sdkEnum.access), !sdkEnum.isFixed, enumType);
|
|
307
|
+
rustEnum = new rust.Enum(enumName, adaptAccessFlags(sdkEnum.access), !sdkEnum.isFixed, enumType, this.adaptNamespace(sdkEnum.namespace));
|
|
192
308
|
rustEnum.docs = this.adaptDocs(sdkEnum.summary, sdkEnum.doc);
|
|
193
309
|
this.types.set(enumName, rustEnum);
|
|
194
310
|
// the first pass is to detect any enum values that coalesce into duplicate entries
|
|
@@ -299,11 +415,10 @@ export class Adapter {
|
|
|
299
415
|
}
|
|
300
416
|
// include error and LRO polling types as output types
|
|
301
417
|
if ((model.usage & tcgc.UsageFlags.Output) === tcgc.UsageFlags.Output ||
|
|
302
|
-
(model.usage & tcgc.UsageFlags.Exception) === tcgc.UsageFlags.Exception ||
|
|
303
418
|
(model.usage & tcgc.UsageFlags.LroPolling) === tcgc.UsageFlags.LroPolling) {
|
|
304
419
|
modelFlags |= rust.ModelFlags.Output;
|
|
305
420
|
}
|
|
306
|
-
rustModel = new rust.Model(modelName, model.access === 'internal' ? 'pubCrate' : 'pub', modelFlags);
|
|
421
|
+
rustModel = new rust.Model(modelName, model.access === 'internal' ? 'pubCrate' : 'pub', modelFlags, this.adaptNamespace(model.namespace));
|
|
307
422
|
rustModel.docs = this.adaptDocs(model.summary, model.doc);
|
|
308
423
|
rustModel.xmlName = getXMLName(model.decorators);
|
|
309
424
|
this.types.set(modelName, rustModel);
|
|
@@ -406,7 +521,7 @@ export class Adapter {
|
|
|
406
521
|
if (!discriminatorProperty) {
|
|
407
522
|
throw new AdapterError('InternalError', `failed to find discriminator field for type ${src.name}`, (_c = src.__raw) === null || _c === void 0 ? void 0 : _c.node);
|
|
408
523
|
}
|
|
409
|
-
rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), discriminatorProperty.name);
|
|
524
|
+
rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), discriminatorProperty.name, this.adaptNamespace(src.namespace));
|
|
410
525
|
if (discriminatorProperty.type.kind === 'enum' && discriminatorProperty.type.isFixed) {
|
|
411
526
|
// when the DU is a fixed enum, it means we don't fall back to the
|
|
412
527
|
// base type when the discriminator value is unknown or missing.
|
|
@@ -432,7 +547,7 @@ export class Adapter {
|
|
|
432
547
|
// at us when accessing src.discriminatedOptions
|
|
433
548
|
throw new AdapterError('InternalError', 'getDiscriminatedUnion called for non-discriminated union', (_e = src.__raw) === null || _e === void 0 ? void 0 : _e.node);
|
|
434
549
|
}
|
|
435
|
-
rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), src.discriminatedOptions.discriminatorPropertyName);
|
|
550
|
+
rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), src.discriminatedOptions.discriminatorPropertyName, this.adaptNamespace(src.namespace));
|
|
436
551
|
if (src.discriminatedOptions.envelopePropertyName) {
|
|
437
552
|
rustUnion.unionKind = new rust.DiscriminatedUnionEnvelope(src.discriminatedOptions.envelopePropertyName);
|
|
438
553
|
}
|
|
@@ -476,6 +591,193 @@ export class Adapter {
|
|
|
476
591
|
var _a, _b, _c, _d, _e;
|
|
477
592
|
return property.kind === 'property' ? (_d = (_b = (_a = property.serializationOptions.json) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : (_c = property.serializationOptions.xml) === null || _c === void 0 ? void 0 : _c.name) !== null && _d !== void 0 ? _d : (_e = property.serializationOptions.multipart) === null || _e === void 0 ? void 0 : _e.name : undefined;
|
|
478
593
|
}
|
|
594
|
+
/**
|
|
595
|
+
* converts a non-discriminated tcgc union to either a flat merged Rust Enum (union-of-enums)
|
|
596
|
+
* or a Rust UntaggedUnion (#[serde(untagged)] enum).
|
|
597
|
+
* self-registers the result in the owning module and caches it.
|
|
598
|
+
*
|
|
599
|
+
* @param src the non-discriminated tcgc union
|
|
600
|
+
* @returns a Rust Enum or UntaggedUnion
|
|
601
|
+
*/
|
|
602
|
+
getNonDiscriminatedUnion(src) {
|
|
603
|
+
const unionName = src.name.length > 0
|
|
604
|
+
? utils.deconstruct(src.name).map(each => utils.capitalize(each)).join('')
|
|
605
|
+
: this.synthesizeUnionName(src);
|
|
606
|
+
const keyName = `non-discriminated-union-${unionName}`;
|
|
607
|
+
const cached = this.types.get(keyName);
|
|
608
|
+
if (cached) {
|
|
609
|
+
return cached;
|
|
610
|
+
}
|
|
611
|
+
const result = src.variantTypes.every(v => v.kind === 'enum')
|
|
612
|
+
? this.getMergedFlatEnum(unionName, src)
|
|
613
|
+
: this.buildUntaggedUnion(unionName, src);
|
|
614
|
+
this.types.set(keyName, result);
|
|
615
|
+
const mod = this.adaptNamespace(src.namespace);
|
|
616
|
+
if (result.kind === 'enum') {
|
|
617
|
+
mod.enums.push(result);
|
|
618
|
+
}
|
|
619
|
+
else {
|
|
620
|
+
mod.unions.push(result);
|
|
621
|
+
}
|
|
622
|
+
return result;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* synthesizes a union name for anonymous inline unions by concatenating variant type names
|
|
626
|
+
*
|
|
627
|
+
* @param src the non-discriminated tcgc union
|
|
628
|
+
* @returns a PascalCase name for the union
|
|
629
|
+
*/
|
|
630
|
+
synthesizeUnionName(src) {
|
|
631
|
+
return src.variantTypes
|
|
632
|
+
.map(v => {
|
|
633
|
+
switch (v.kind) {
|
|
634
|
+
case 'model':
|
|
635
|
+
case 'enum':
|
|
636
|
+
return utils.deconstruct(v.name).map(each => utils.capitalize(each)).join('');
|
|
637
|
+
case 'string':
|
|
638
|
+
return 'String';
|
|
639
|
+
case 'boolean':
|
|
640
|
+
return 'Boolean';
|
|
641
|
+
case 'int32':
|
|
642
|
+
return 'Int32';
|
|
643
|
+
case 'int64':
|
|
644
|
+
return 'Int64';
|
|
645
|
+
case 'float32':
|
|
646
|
+
return 'Float32';
|
|
647
|
+
case 'float64':
|
|
648
|
+
return 'Float64';
|
|
649
|
+
case 'array':
|
|
650
|
+
return 'Array';
|
|
651
|
+
default:
|
|
652
|
+
return 'Value';
|
|
653
|
+
}
|
|
654
|
+
})
|
|
655
|
+
.join('');
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* builds a flat merged Rust Enum from a union whose every variant is a TSP enum type.
|
|
659
|
+
* all variant enum values are merged into a single flat enum.
|
|
660
|
+
*
|
|
661
|
+
* @param unionName the PascalCase name for the resulting enum
|
|
662
|
+
* @param src the non-discriminated tcgc union
|
|
663
|
+
* @returns a flat merged Rust Enum
|
|
664
|
+
*/
|
|
665
|
+
getMergedFlatEnum(unionName, src) {
|
|
666
|
+
const rustEnum = new rust.Enum(unionName, adaptAccessFlags(src.access), false, 'String', this.adaptNamespace(src.namespace));
|
|
667
|
+
rustEnum.docs = this.adaptDocs(src.summary, src.doc);
|
|
668
|
+
this.types.set(unionName, rustEnum);
|
|
669
|
+
for (const variant of src.variantTypes) {
|
|
670
|
+
const sdkEnum = variant;
|
|
671
|
+
for (const value of sdkEnum.values) {
|
|
672
|
+
const valueName = naming.fixUpEnumValueName(value);
|
|
673
|
+
if (!rustEnum.values.some(v => v.name === valueName)) {
|
|
674
|
+
const rv = new rust.EnumValue(valueName, rustEnum, value.value);
|
|
675
|
+
rv.docs = this.adaptDocs(value.summary, value.doc);
|
|
676
|
+
rustEnum.values.push(rv);
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
return rustEnum;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* builds an UntaggedUnion (#[serde(untagged)] enum) from a non-discriminated tcgc union
|
|
684
|
+
* whose variants are not all enum types.
|
|
685
|
+
*
|
|
686
|
+
* @param unionName the PascalCase name for the resulting union
|
|
687
|
+
* @param src the non-discriminated tcgc union
|
|
688
|
+
* @returns a Rust UntaggedUnion
|
|
689
|
+
*/
|
|
690
|
+
buildUntaggedUnion(unionName, src) {
|
|
691
|
+
const mod = this.adaptNamespace(src.namespace);
|
|
692
|
+
const rustUnion = new rust.UntaggedUnion(unionName, adaptAccessFlags(src.access), mod);
|
|
693
|
+
rustUnion.docs = this.adaptDocs(src.summary, src.doc);
|
|
694
|
+
// sort variants for correct serde untagged disambiguation:
|
|
695
|
+
// bool(0) < int(1) < float(2) < enum(3) < string(4) < array(5) < model(6)
|
|
696
|
+
const sorted = [...src.variantTypes].sort((a, b) => this.getUntaggedVariantOrder(a) - this.getUntaggedVariantOrder(b));
|
|
697
|
+
for (const variant of sorted) {
|
|
698
|
+
const variantName = this.getUntaggedVariantName(variant);
|
|
699
|
+
const variantType = this.typeToWireType(this.getType(variant));
|
|
700
|
+
const rv = new rust.UntaggedUnionVariant(variantName, variantType);
|
|
701
|
+
rustUnion.variants.push(rv);
|
|
702
|
+
}
|
|
703
|
+
return rustUnion;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* returns the sort order for a variant in a #[serde(untagged)] enum.
|
|
707
|
+
* more specific types must come before more general ones to ensure correct deserialization.
|
|
708
|
+
*
|
|
709
|
+
* @param variant the tcgc type to get the order for
|
|
710
|
+
* @returns a numeric sort key (lower = earlier)
|
|
711
|
+
*/
|
|
712
|
+
getUntaggedVariantOrder(variant) {
|
|
713
|
+
switch (variant.kind) {
|
|
714
|
+
case 'boolean':
|
|
715
|
+
return 0;
|
|
716
|
+
case 'constant':
|
|
717
|
+
return this.getUntaggedVariantOrder(variant.valueType);
|
|
718
|
+
case 'int8':
|
|
719
|
+
case 'int16':
|
|
720
|
+
case 'int32':
|
|
721
|
+
case 'int64':
|
|
722
|
+
case 'safeint':
|
|
723
|
+
return 1;
|
|
724
|
+
case 'float':
|
|
725
|
+
case 'float32':
|
|
726
|
+
case 'float64':
|
|
727
|
+
return 2;
|
|
728
|
+
case 'enum':
|
|
729
|
+
return 3;
|
|
730
|
+
case 'string':
|
|
731
|
+
return 4;
|
|
732
|
+
case 'array':
|
|
733
|
+
return 5;
|
|
734
|
+
case 'model':
|
|
735
|
+
return 6;
|
|
736
|
+
default:
|
|
737
|
+
return 7;
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
/**
|
|
741
|
+
* returns the Rust variant name (PascalCase) for a variant in a #[serde(untagged)] enum.
|
|
742
|
+
*
|
|
743
|
+
* @param variant the tcgc type to name
|
|
744
|
+
* @returns a PascalCase Rust variant name
|
|
745
|
+
*/
|
|
746
|
+
getUntaggedVariantName(variant) {
|
|
747
|
+
switch (variant.kind) {
|
|
748
|
+
case 'model':
|
|
749
|
+
return utils.deconstruct(variant.name).map(each => utils.capitalize(each)).join('');
|
|
750
|
+
case 'enum':
|
|
751
|
+
return utils.deconstruct(variant.name).map(each => utils.capitalize(each)).join('');
|
|
752
|
+
case 'boolean':
|
|
753
|
+
return 'Boolean';
|
|
754
|
+
case 'int8':
|
|
755
|
+
return 'Int8';
|
|
756
|
+
case 'int16':
|
|
757
|
+
return 'Int16';
|
|
758
|
+
case 'int32':
|
|
759
|
+
return 'Int32';
|
|
760
|
+
case 'int64':
|
|
761
|
+
return 'Int64';
|
|
762
|
+
case 'safeint':
|
|
763
|
+
return 'SafeInt';
|
|
764
|
+
case 'float':
|
|
765
|
+
case 'float32':
|
|
766
|
+
return 'Float32';
|
|
767
|
+
case 'float64':
|
|
768
|
+
return 'Float64';
|
|
769
|
+
case 'string':
|
|
770
|
+
return 'String';
|
|
771
|
+
case 'constant':
|
|
772
|
+
return this.getUntaggedVariantName(variant.valueType);
|
|
773
|
+
case 'array': {
|
|
774
|
+
const elemName = this.getUntaggedVariantName(variant.valueType);
|
|
775
|
+
return `${elemName}Array`;
|
|
776
|
+
}
|
|
777
|
+
default:
|
|
778
|
+
return 'Value';
|
|
779
|
+
}
|
|
780
|
+
}
|
|
479
781
|
/**
|
|
480
782
|
* converts a tcgc model property to a model field
|
|
481
783
|
*
|
|
@@ -488,7 +790,7 @@ export class Adapter {
|
|
|
488
790
|
getModelField(modelFlags, property, modelVisibility, stack) {
|
|
489
791
|
var _a, _b, _c, _d, _e;
|
|
490
792
|
const fieldNeedsBoxing = function (fieldType) {
|
|
491
|
-
if (fieldType.kind === 'model' && stack.includes(fieldType)) {
|
|
793
|
+
if (fieldType.kind === 'model' && (stack.includes(fieldType))) {
|
|
492
794
|
// if the field's type is a model and it's in the type stack then
|
|
493
795
|
// box it. this is to avoid infinitely recursive type definitions.
|
|
494
796
|
return true;
|
|
@@ -518,6 +820,17 @@ export class Adapter {
|
|
|
518
820
|
const serializedName = (_a = this.getSerializedPropertyName(property)) !== null && _a !== void 0 ? _a : property.name;
|
|
519
821
|
const modelField = new rust.ModelField(naming.getEscapedReservedName(utils.snakeCaseName(property.name), 'prop'), serializedName, modelVisibility, fieldType, property.optional);
|
|
520
822
|
modelField.docs = this.adaptDocs(property.summary, property.doc);
|
|
823
|
+
// append visibility info as a doc comment when visibility is restricted
|
|
824
|
+
const visibilityStr = formatVisibility(property.visibility);
|
|
825
|
+
if (visibilityStr) {
|
|
826
|
+
if (!modelField.docs.description) {
|
|
827
|
+
modelField.docs.description = '';
|
|
828
|
+
}
|
|
829
|
+
else {
|
|
830
|
+
modelField.docs.description += '\n\n';
|
|
831
|
+
}
|
|
832
|
+
modelField.docs.description += `Operational visibility: ${visibilityStr}`;
|
|
833
|
+
}
|
|
521
834
|
// if this is a literal, add a doc comment explaining its behavior
|
|
522
835
|
const unwrappedType = utils.unwrapOption(fieldType);
|
|
523
836
|
if (unwrappedType.kind === 'enumValue' || unwrappedType.kind === 'literal') {
|
|
@@ -593,7 +906,7 @@ export class Adapter {
|
|
|
593
906
|
* @returns the adapted Rust type
|
|
594
907
|
*/
|
|
595
908
|
getType(type, stack) {
|
|
596
|
-
var _a, _b
|
|
909
|
+
var _a, _b;
|
|
597
910
|
if (type.external) {
|
|
598
911
|
return this.getExternalType(type.external);
|
|
599
912
|
}
|
|
@@ -675,6 +988,9 @@ export class Adapter {
|
|
|
675
988
|
else if (type.discriminatedSubtypes) {
|
|
676
989
|
return this.getDiscriminatedUnion(type);
|
|
677
990
|
}
|
|
991
|
+
else if (tcgc.isAzureCoreModel(type)) {
|
|
992
|
+
return this.getExternalType({ kind: 'externalTypeInfo', identity: 'azure_core::error::ErrorDetail' });
|
|
993
|
+
}
|
|
678
994
|
return this.getModel(type, stack);
|
|
679
995
|
case 'endpoint':
|
|
680
996
|
case 'plainDate':
|
|
@@ -728,12 +1044,12 @@ export class Adapter {
|
|
|
728
1044
|
}
|
|
729
1045
|
case 'union': {
|
|
730
1046
|
if (!type.discriminatedOptions) {
|
|
731
|
-
|
|
1047
|
+
return this.getNonDiscriminatedUnion(type);
|
|
732
1048
|
}
|
|
733
1049
|
return this.getDiscriminatedUnion(type);
|
|
734
1050
|
}
|
|
735
1051
|
default:
|
|
736
|
-
throw new AdapterError('UnsupportedTsp', `unhandled tcgc type ${type.kind}`, (
|
|
1052
|
+
throw new AdapterError('UnsupportedTsp', `unhandled tcgc type ${type.kind}`, (_b = type.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
737
1053
|
}
|
|
738
1054
|
}
|
|
739
1055
|
/** returns a Box type */
|
|
@@ -801,11 +1117,12 @@ export class Adapter {
|
|
|
801
1117
|
return optionType;
|
|
802
1118
|
}
|
|
803
1119
|
/** returns the specified type wrapped in a Ref */
|
|
804
|
-
getRefType(type) {
|
|
805
|
-
const typeKey = recursiveKeyName(
|
|
1120
|
+
getRefType(type, lifetime) {
|
|
1121
|
+
const typeKey = recursiveKeyName(`ref${lifetime ? `-${lifetime.name}` : ''}`, type);
|
|
806
1122
|
let refType = this.types.get(typeKey);
|
|
807
1123
|
if (!refType) {
|
|
808
1124
|
refType = new rust.Ref(type);
|
|
1125
|
+
refType.lifetime = lifetime;
|
|
809
1126
|
this.types.set(typeKey, refType);
|
|
810
1127
|
}
|
|
811
1128
|
return refType;
|
|
@@ -954,8 +1271,14 @@ export class Adapter {
|
|
|
954
1271
|
}
|
|
955
1272
|
/** converts all tcgc clients and their methods into Rust clients/methods */
|
|
956
1273
|
adaptClients() {
|
|
1274
|
+
let needsCore = false;
|
|
957
1275
|
for (const client of this.ctx.sdkPackage.clients) {
|
|
1276
|
+
// start with instantiable clients and recursively work down
|
|
958
1277
|
this.recursiveAdaptClient(client);
|
|
1278
|
+
needsCore = true;
|
|
1279
|
+
}
|
|
1280
|
+
if (needsCore) {
|
|
1281
|
+
this.crate.addDependency(new rust.CrateDependency('azure_core'));
|
|
959
1282
|
}
|
|
960
1283
|
}
|
|
961
1284
|
/**
|
|
@@ -1005,7 +1328,7 @@ export class Adapter {
|
|
|
1005
1328
|
if (!clientName.match(/Client$/)) {
|
|
1006
1329
|
clientName += 'Client';
|
|
1007
1330
|
}
|
|
1008
|
-
const rustClient = new rust.Client(clientName);
|
|
1331
|
+
const rustClient = new rust.Client(clientName, this.adaptNamespace(client.namespace));
|
|
1009
1332
|
rustClient.docs = this.adaptDocs(client.summary, client.doc);
|
|
1010
1333
|
rustClient.parent = parent;
|
|
1011
1334
|
rustClient.fields.push(new rust.StructField('pipeline', 'pubCrate', new rust.ExternalType(this.crate, 'Pipeline', 'azure_core::http')));
|
|
@@ -1210,7 +1533,7 @@ export class Adapter {
|
|
|
1210
1533
|
}
|
|
1211
1534
|
// Set the tracing namespace for tracing based on the client's namespace
|
|
1212
1535
|
rustClient.languageIndependentName = client.crossLanguageDefinitionId;
|
|
1213
|
-
this.
|
|
1536
|
+
this.adaptNamespace(client.namespace).clients.push(rustClient);
|
|
1214
1537
|
return rustClient;
|
|
1215
1538
|
}
|
|
1216
1539
|
/**
|
|
@@ -1259,7 +1582,9 @@ export class Adapter {
|
|
|
1259
1582
|
paramField.docs = this.adaptDocs(param.summary, param.doc);
|
|
1260
1583
|
constructable.options.type.fields.push(paramField);
|
|
1261
1584
|
if (param.clientDefaultValue) {
|
|
1262
|
-
|
|
1585
|
+
const constName = `DEFAULT_${paramName.toUpperCase()}`;
|
|
1586
|
+
paramField.defaultValue = `String::from(${constName})`;
|
|
1587
|
+
paramField.defaultValueConstant = { name: constName, value: param.clientDefaultValue };
|
|
1263
1588
|
}
|
|
1264
1589
|
}
|
|
1265
1590
|
let adaptedParam;
|
|
@@ -1312,7 +1637,7 @@ export class Adapter {
|
|
|
1312
1637
|
* @param rustClient the client to which the method belongs
|
|
1313
1638
|
*/
|
|
1314
1639
|
adaptMethod(method, rustClient) {
|
|
1315
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s
|
|
1640
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
|
1316
1641
|
let srcMethodName = method.name;
|
|
1317
1642
|
if (method.kind === 'paging' && !srcMethodName.match(/^list/i)) {
|
|
1318
1643
|
const chunks = utils.deconstruct(srcMethodName);
|
|
@@ -1344,7 +1669,7 @@ export class Adapter {
|
|
|
1344
1669
|
const optionsLifetime = new rust.Lifetime('a');
|
|
1345
1670
|
const methodOptionsStruct = new rust.Struct(`${rustClient.name}${utils.pascalCase(srcMethodName, false)}Options`, pub);
|
|
1346
1671
|
methodOptionsStruct.lifetime = optionsLifetime;
|
|
1347
|
-
methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`,
|
|
1672
|
+
methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`, `${utils.buildImportPath(rustClient.module, rustClient.module)}::clients::${rustClient.name}::${methodName}()`)}`;
|
|
1348
1673
|
let clientMethodOptions;
|
|
1349
1674
|
switch (method.kind) {
|
|
1350
1675
|
case 'paging':
|
|
@@ -1360,7 +1685,7 @@ export class Adapter {
|
|
|
1360
1685
|
const methodOptionsField = new rust.StructField('method_options', pub, clientMethodOptions);
|
|
1361
1686
|
methodOptionsField.docs.summary = 'Allows customization of the method call.';
|
|
1362
1687
|
methodOptionsStruct.fields.push(methodOptionsField);
|
|
1363
|
-
const methodOptions = new rust.
|
|
1688
|
+
const methodOptions = new rust.ParameterGroup('options', new rust.Option(methodOptionsStruct));
|
|
1364
1689
|
const httpMethod = method.operation.verb;
|
|
1365
1690
|
let rustMethod;
|
|
1366
1691
|
switch (method.kind) {
|
|
@@ -1411,44 +1736,21 @@ export class Adapter {
|
|
|
1411
1736
|
}
|
|
1412
1737
|
// maps tcgc method header/query params to their Rust method params
|
|
1413
1738
|
const paramsMap = new Map();
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
//
|
|
1417
|
-
//
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
});
|
|
1423
|
-
});
|
|
1424
|
-
if (!opParam) {
|
|
1425
|
-
throw new AdapterError('InternalError', `didn't find operation parameter for method ${method.name} parameter ${param.name}`, (_f = param.__raw) === null || _f === void 0 ? void 0 : _f.node);
|
|
1426
|
-
}
|
|
1427
|
-
if (opParam.kind === 'header' && opParam.serializedName.toLowerCase() === 'x-ms-client-request-id') {
|
|
1428
|
-
// x-ms-client-request-id is automatically inserted into requests via
|
|
1429
|
-
// a pipeline policy. so we don't want to expose this as an actual param.
|
|
1430
|
-
continue;
|
|
1431
|
-
}
|
|
1432
|
-
let adaptedParam;
|
|
1433
|
-
// for spread params there are two cases we need to consider.
|
|
1434
|
-
// if the method param's type doesn't match the op param's type then it's a spread param
|
|
1435
|
-
// - e.g. method param's type is string/int/etc which is a field in the op param's body type
|
|
1436
|
-
// if the method param's type DOES match the op param's type and the op param has multiple corresponding method params, it's a spread param
|
|
1437
|
-
// - e.g. op param is an intersection of multiple model types, and each model type is exposed as a discrete param
|
|
1438
|
-
if (opParam.kind === 'body' && opParam.type.kind === 'model'
|
|
1439
|
-
&& (opParam.type.kind !== param.type.kind || opParam.methodParameterSegments.map((segment) => segment[segment.length - 1]).length > 1)) {
|
|
1440
|
-
adaptedParam = this.adaptMethodSpreadParameter(param, this.adaptPayloadFormat(opParam.defaultContentType), opParam.type);
|
|
1441
|
-
}
|
|
1442
|
-
else {
|
|
1443
|
-
adaptedParam = this.adaptMethodParameter(opParam);
|
|
1444
|
-
}
|
|
1739
|
+
/** returns true if the op param should be omitted */
|
|
1740
|
+
const skipOpParam = function (opParam) {
|
|
1741
|
+
// x-ms-client-request-id is automatically inserted into requests via
|
|
1742
|
+
// a pipeline policy. so we don't want to expose this as an actual param.
|
|
1743
|
+
return opParam.kind === 'header' && opParam.serializedName.toLowerCase() === 'x-ms-client-request-id';
|
|
1744
|
+
};
|
|
1745
|
+
/** adds adapted params to the method's param list and optional params to method options */
|
|
1746
|
+
const processAdaptedParam = (adaptedParam, methodParam) => {
|
|
1445
1747
|
switch (adaptedParam.kind) {
|
|
1446
1748
|
case 'headerScalar':
|
|
1447
1749
|
case 'queryScalar':
|
|
1448
|
-
paramsMap.set(
|
|
1750
|
+
paramsMap.set(methodParam, adaptedParam);
|
|
1449
1751
|
break;
|
|
1450
1752
|
}
|
|
1451
|
-
adaptedParam.docs = this.adaptDocs(
|
|
1753
|
+
adaptedParam.docs = this.adaptDocs(methodParam.summary, methodParam.doc);
|
|
1452
1754
|
rustMethod.params.push(adaptedParam);
|
|
1453
1755
|
// we specially handle an optional content-type header to ensure it's omitted
|
|
1454
1756
|
// from the options bag type. this shows up when the request body is optional.
|
|
@@ -1460,9 +1762,9 @@ export class Adapter {
|
|
|
1460
1762
|
// sent in the request. we want the field within the model for this param.
|
|
1461
1763
|
// NOTE: if the param is optional then the field is optional, thus it's
|
|
1462
1764
|
// already wrapped in an Option<T> type.
|
|
1463
|
-
const field = adaptedParam.type.content.
|
|
1765
|
+
const field = adaptedParam.type.content.fields.find(f => { return f.name === adaptedParam.name; });
|
|
1464
1766
|
if (!field) {
|
|
1465
|
-
throw new AdapterError('InternalError', `didn't find spread param field ${adaptedParam.name} in type ${adaptedParam.type.content.
|
|
1767
|
+
throw new AdapterError('InternalError', `didn't find spread param field ${adaptedParam.name} in type ${adaptedParam.type.content.name}`);
|
|
1466
1768
|
}
|
|
1467
1769
|
fieldType = field.type;
|
|
1468
1770
|
}
|
|
@@ -1471,7 +1773,124 @@ export class Adapter {
|
|
|
1471
1773
|
}
|
|
1472
1774
|
const optionsField = new rust.StructField(adaptedParam.name, pub, fieldType);
|
|
1473
1775
|
optionsField.docs = adaptedParam.docs;
|
|
1474
|
-
rustMethod.options.type.fields.push(optionsField);
|
|
1776
|
+
rustMethod.options.type.type.fields.push(optionsField);
|
|
1777
|
+
}
|
|
1778
|
+
};
|
|
1779
|
+
for (const param of method.parameters) {
|
|
1780
|
+
// we need to translate from the method param to its underlying operation param.
|
|
1781
|
+
// most params have a one-to-one mapping. however, for spread params, there will
|
|
1782
|
+
// be a many-to-one mapping. i.e. multiple params will map to the same underlying
|
|
1783
|
+
// operation param. each param corresponds to a field within the operation param.
|
|
1784
|
+
// there are three distinct cases
|
|
1785
|
+
// - one-to-one mapping (header param -> header op param)
|
|
1786
|
+
// - many-to-one mapping (spread params, multiple params map to one op param)
|
|
1787
|
+
// - one-to-many mapping (grouped params, one param maps to multiple op params)
|
|
1788
|
+
// two-phase filter: first try strict match on the last segment element (cases 1 and 2).
|
|
1789
|
+
// if no matches, try matching the first segment element (case 3: parameter group from @@override).
|
|
1790
|
+
// this avoids false case-3 matches when a body model has fields extracted as HTTP params
|
|
1791
|
+
// (e.g. @query fields), where segment[0] is the body param name but it's not a parameter group.
|
|
1792
|
+
let opParams = allOpParams.filter((opParam) => {
|
|
1793
|
+
return opParam.methodParameterSegments.some((segment) => segment[segment.length - 1].name === param.name);
|
|
1794
|
+
});
|
|
1795
|
+
if (opParams.length === 0) {
|
|
1796
|
+
opParams = allOpParams.filter((opParam) => {
|
|
1797
|
+
return opParam.methodParameterSegments.some((segment) => segment[0].name === param.name);
|
|
1798
|
+
});
|
|
1799
|
+
}
|
|
1800
|
+
let adaptedParam;
|
|
1801
|
+
if (opParams.length === 1) {
|
|
1802
|
+
// case 1 or 2
|
|
1803
|
+
const opParam = opParams[0];
|
|
1804
|
+
if (skipOpParam(opParam)) {
|
|
1805
|
+
continue;
|
|
1806
|
+
}
|
|
1807
|
+
// for spread params there are two cases we need to consider.
|
|
1808
|
+
// if the method param's type doesn't match the op param's type then it's a spread param
|
|
1809
|
+
// - e.g. method param's type is string/int/etc which is a field in the op param's body type
|
|
1810
|
+
// if the method param's type DOES match the op param's type and the op param has multiple corresponding method params, it's a spread param
|
|
1811
|
+
// - e.g. op param is an intersection of multiple model types, and each model type is exposed as a discrete param
|
|
1812
|
+
if (opParam.kind === 'body' && opParam.type.kind === 'model'
|
|
1813
|
+
&& (opParam.type !== param.type || opParam.methodParameterSegments.map((segment) => segment[segment.length - 1]).length > 1)) {
|
|
1814
|
+
adaptedParam = this.adaptMethodSpreadParameter(param, this.getPayloadFormatType(opParam.type, opParam.defaultContentType), opParam.type);
|
|
1815
|
+
}
|
|
1816
|
+
else {
|
|
1817
|
+
adaptedParam = this.adaptMethodParameter(opParam, param);
|
|
1818
|
+
}
|
|
1819
|
+
processAdaptedParam(adaptedParam, param);
|
|
1820
|
+
}
|
|
1821
|
+
else if (opParams.length > 1) {
|
|
1822
|
+
// case 3 (param group)
|
|
1823
|
+
// Parameter group handling:
|
|
1824
|
+
// - required params stay in the named parameter group
|
|
1825
|
+
// - optional params are moved to the method's options type
|
|
1826
|
+
// - if no required params remain, the param group evaporates
|
|
1827
|
+
let paramGroup;
|
|
1828
|
+
const groupedParams = new Array();
|
|
1829
|
+
const lifetime = new rust.Lifetime('a');
|
|
1830
|
+
let hasRefs = false;
|
|
1831
|
+
for (const opParam of opParams) {
|
|
1832
|
+
if (skipOpParam(opParam)) {
|
|
1833
|
+
continue;
|
|
1834
|
+
}
|
|
1835
|
+
// for grouped params, the segment is [groupMethodParam, fieldProp].
|
|
1836
|
+
// we need the field property to get the correct type for this specific
|
|
1837
|
+
// operation parameter (the opParam's own type might be the group model type).
|
|
1838
|
+
const fieldProp = opParam.methodParameterSegments
|
|
1839
|
+
.flatMap((segment) => segment.length > 1 ? [segment[segment.length - 1]] : [])
|
|
1840
|
+
.find((prop) => prop.name === opParam.name);
|
|
1841
|
+
// pass fieldProp as methodParam so adaptMethodParameter uses the field's type.
|
|
1842
|
+
// cast is safe as we only need .type, .optional, .summary, .doc from it.
|
|
1843
|
+
const groupedParam = this.adaptMethodParameter(opParam, fieldProp, lifetime);
|
|
1844
|
+
const fieldOptional = fieldProp ? fieldProp.optional : opParam.optional;
|
|
1845
|
+
if (!fieldOptional) {
|
|
1846
|
+
if (!paramGroup) {
|
|
1847
|
+
if (param.type.kind !== 'model') {
|
|
1848
|
+
throw new AdapterError('InternalError', `expected model type for parameter group but got ${param.type.kind}`, (_f = param.__raw) === null || _f === void 0 ? void 0 : _f.node);
|
|
1849
|
+
}
|
|
1850
|
+
const groupName = utils.capitalize(param.type.name).replace(/\W/g, '');
|
|
1851
|
+
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(param.name), 'param', reservedParams);
|
|
1852
|
+
// param group's visibility is tied to its matching method
|
|
1853
|
+
const groupStruct = new rust.Struct(groupName, rustMethod.visibility);
|
|
1854
|
+
groupStruct.docs = this.adaptDocs(param.type.summary, param.type.doc);
|
|
1855
|
+
paramGroup = new rust.ParameterGroup(paramName, groupStruct);
|
|
1856
|
+
paramGroup.docs = this.adaptDocs(param.summary, param.doc);
|
|
1857
|
+
}
|
|
1858
|
+
groupedParam.docs = this.adaptDocs(opParam.summary, opParam.doc);
|
|
1859
|
+
groupedParams.push(groupedParam);
|
|
1860
|
+
groupedParam.group = paramGroup;
|
|
1861
|
+
if (groupedParam.type.kind === 'ref') {
|
|
1862
|
+
hasRefs = true;
|
|
1863
|
+
}
|
|
1864
|
+
}
|
|
1865
|
+
else {
|
|
1866
|
+
// optional params within a group are moved to the method's options type
|
|
1867
|
+
processAdaptedParam(groupedParam, param);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
if (paramGroup) {
|
|
1871
|
+
if (hasRefs) {
|
|
1872
|
+
paramGroup.type.lifetime = lifetime;
|
|
1873
|
+
}
|
|
1874
|
+
// push each grouped param individually; they already have .group set
|
|
1875
|
+
for (const gp of groupedParams) {
|
|
1876
|
+
rustMethod.params.push(gp);
|
|
1877
|
+
}
|
|
1878
|
+
// remove the group's model type from the module's models list.
|
|
1879
|
+
// the parameter group struct will be emitted by the client codegen
|
|
1880
|
+
// in method_options.rs. leaving the model in models.rs would create
|
|
1881
|
+
// a duplicate struct definition with incompatible field types.
|
|
1882
|
+
if (param.type.kind === 'model') {
|
|
1883
|
+
const groupModelName = utils.capitalize(param.type.name).replace(/\W/g, '');
|
|
1884
|
+
const ns = this.adaptNamespace(param.type.namespace);
|
|
1885
|
+
const idx = ns.models.findIndex(m => m.kind === 'model' && m.name === groupModelName);
|
|
1886
|
+
if (idx >= 0) {
|
|
1887
|
+
ns.models.splice(idx, 1);
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
else {
|
|
1893
|
+
throw new AdapterError('InternalError', `didn't find operation parameter for method ${method.name} parameter ${param.name}`, (_g = param.__raw) === null || _g === void 0 ? void 0 : _g.node);
|
|
1475
1894
|
}
|
|
1476
1895
|
}
|
|
1477
1896
|
// client params aren't included in method.parameters so
|
|
@@ -1487,6 +1906,7 @@ export class Adapter {
|
|
|
1487
1906
|
var _a;
|
|
1488
1907
|
// fetch the body format from the HTTP responses.
|
|
1489
1908
|
// they should all have the same type so no need to match responses to type.
|
|
1909
|
+
let responseType;
|
|
1490
1910
|
let defaultContentType;
|
|
1491
1911
|
for (const httpResp of method.operation.responses) {
|
|
1492
1912
|
if (!httpResp.defaultContentType) {
|
|
@@ -1498,11 +1918,12 @@ export class Adapter {
|
|
|
1498
1918
|
throw new AdapterError('InternalError', `method ${method.name} has conflicting content types`, (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
1499
1919
|
}
|
|
1500
1920
|
defaultContentType = httpResp.defaultContentType;
|
|
1921
|
+
responseType = httpResp.type;
|
|
1501
1922
|
}
|
|
1502
1923
|
if (!defaultContentType) {
|
|
1503
1924
|
return 'NoFormat';
|
|
1504
1925
|
}
|
|
1505
|
-
return this.
|
|
1926
|
+
return this.getPayloadFormatType(responseType, defaultContentType);
|
|
1506
1927
|
};
|
|
1507
1928
|
const getStatusCodes = function (httpOp) {
|
|
1508
1929
|
const statusCodes = new Array();
|
|
@@ -1551,25 +1972,26 @@ export class Adapter {
|
|
|
1551
1972
|
}
|
|
1552
1973
|
const responseFormat = getResponseFormat();
|
|
1553
1974
|
if (method.kind === 'paging') {
|
|
1554
|
-
if (responseFormat
|
|
1555
|
-
throw new AdapterError('InternalError', `paged method ${method.name} unexpected response format ${responseFormat}`, (
|
|
1975
|
+
if (responseFormat !== 'JsonFormat' && responseFormat !== 'XmlFormat') {
|
|
1976
|
+
throw new AdapterError('InternalError', `paged method ${method.name} unexpected response format ${responseFormat}`, (_h = method.__raw) === null || _h === void 0 ? void 0 : _h.node);
|
|
1556
1977
|
}
|
|
1557
1978
|
// for paged methods, tcgc models method.response.type as an Array<T>.
|
|
1558
1979
|
// however, we want the synthesized paged response envelope type instead.
|
|
1559
1980
|
const synthesizedType = method.operation.responses[0].type;
|
|
1560
1981
|
if (!synthesizedType) {
|
|
1561
|
-
throw new AdapterError('InternalError', `paged method ${method.name} has no synthesized response type`, (
|
|
1982
|
+
throw new AdapterError('InternalError', `paged method ${method.name} has no synthesized response type`, (_j = method.__raw) === null || _j === void 0 ? void 0 : _j.node);
|
|
1562
1983
|
}
|
|
1563
1984
|
else if (synthesizedType.kind !== 'model') {
|
|
1564
|
-
throw new AdapterError('UnsupportedTsp', `paged method ${method.name} synthesized response type has unexpected kind ${synthesizedType.kind}`, (
|
|
1985
|
+
throw new AdapterError('UnsupportedTsp', `paged method ${method.name} synthesized response type has unexpected kind ${synthesizedType.kind}`, (_k = method.__raw) === null || _k === void 0 ? void 0 : _k.node);
|
|
1565
1986
|
}
|
|
1566
1987
|
const synthesizedModel = this.getModel(synthesizedType, new Array());
|
|
1567
|
-
|
|
1568
|
-
|
|
1988
|
+
const modelNs = this.adaptNamespace(synthesizedType.namespace);
|
|
1989
|
+
if (!modelNs.models.includes(synthesizedModel)) {
|
|
1990
|
+
modelNs.models.push(synthesizedModel);
|
|
1569
1991
|
}
|
|
1570
1992
|
// for the pager response type, remove the Option<T> around the Vec<T> for the page items
|
|
1571
1993
|
if (!method.pagingMetadata.pageItemsSegments) {
|
|
1572
|
-
throw new AdapterError('InternalError', `paged method ${method.name} has no pageItemsSegments`, (
|
|
1994
|
+
throw new AdapterError('InternalError', `paged method ${method.name} has no pageItemsSegments`, (_l = method.__raw) === null || _l === void 0 ? void 0 : _l.node);
|
|
1573
1995
|
}
|
|
1574
1996
|
// unwrap all of the segments for the paged response
|
|
1575
1997
|
let unwrappedCount = 0;
|
|
@@ -1580,13 +2002,13 @@ export class Adapter {
|
|
|
1580
2002
|
switch (responseFormat) {
|
|
1581
2003
|
case 'JsonFormat':
|
|
1582
2004
|
if (segment.kind !== 'property' || !segment.serializationOptions.json) {
|
|
1583
|
-
throw new AdapterError('InternalError', `paged method ${method.name} is missing JSON serialization data`, (
|
|
2005
|
+
throw new AdapterError('InternalError', `paged method ${method.name} is missing JSON serialization data`, (_m = method.__raw) === null || _m === void 0 ? void 0 : _m.node);
|
|
1584
2006
|
}
|
|
1585
2007
|
serde = segment.serializationOptions.json.name;
|
|
1586
2008
|
break;
|
|
1587
2009
|
case 'XmlFormat':
|
|
1588
2010
|
if (segment.kind !== 'property' || !segment.serializationOptions.xml) {
|
|
1589
|
-
throw new AdapterError('InternalError', `paged method ${method.name} is missing XML serialization data`, (
|
|
2011
|
+
throw new AdapterError('InternalError', `paged method ${method.name} is missing XML serialization data`, (_o = method.__raw) === null || _o === void 0 ? void 0 : _o.node);
|
|
1590
2012
|
}
|
|
1591
2013
|
serde = segment.serializationOptions.xml.name;
|
|
1592
2014
|
break;
|
|
@@ -1612,45 +2034,55 @@ export class Adapter {
|
|
|
1612
2034
|
}
|
|
1613
2035
|
}
|
|
1614
2036
|
if (unwrappedCount !== method.pagingMetadata.pageItemsSegments.length) {
|
|
1615
|
-
throw new AdapterError('InternalError', `failed to unwrap paged items for method ${method.name}`, (
|
|
2037
|
+
throw new AdapterError('InternalError', `failed to unwrap paged items for method ${method.name}`, (_p = method.__raw) === null || _p === void 0 ? void 0 : _p.node);
|
|
1616
2038
|
}
|
|
1617
2039
|
this.crate.addDependency(new rust.CrateDependency('async-trait'));
|
|
1618
2040
|
// default to nextLink. will update it as required when we have that info
|
|
1619
2041
|
rustMethod.returns = new rust.Result(this.crate, new rust.Pager(this.crate, new rust.Response(this.crate, synthesizedModel, responseFormat), 'nextLink'));
|
|
1620
2042
|
}
|
|
1621
2043
|
else if (method.kind === 'lro') {
|
|
1622
|
-
const
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
2044
|
+
const pushModels = (tcgcType, container, rustType = this.getType(tcgcType), ignoreAzureCoreModels = true) => {
|
|
2045
|
+
switch (tcgcType.kind) {
|
|
2046
|
+
case 'array':
|
|
2047
|
+
pushModels(tcgcType.valueType, container);
|
|
2048
|
+
return;
|
|
2049
|
+
case 'model':
|
|
2050
|
+
if (ignoreAzureCoreModels && tcgc.isAzureCoreModel(tcgcType)) {
|
|
2051
|
+
return;
|
|
2052
|
+
}
|
|
2053
|
+
break;
|
|
2054
|
+
default:
|
|
2055
|
+
return;
|
|
2056
|
+
}
|
|
2057
|
+
if (rustType.kind !== 'model' || container.models.some(m => m === rustType) || rustType.module !== rustClient.module) {
|
|
2058
|
+
// model already exists or belongs to a different module, do nothing
|
|
1629
2059
|
return;
|
|
1630
2060
|
}
|
|
1631
|
-
|
|
1632
|
-
for (const
|
|
1633
|
-
|
|
1634
|
-
fieldType = fieldType.kind === 'option' ? fieldType.type : fieldType;
|
|
1635
|
-
fieldType = fieldType.kind === 'Vec' ? fieldType.type : fieldType;
|
|
1636
|
-
if (fieldType.kind === 'model') {
|
|
1637
|
-
pushModels(fieldType, crate);
|
|
1638
|
-
}
|
|
2061
|
+
container.models.push(rustType);
|
|
2062
|
+
for (const tcgcField of tcgcType.properties) {
|
|
2063
|
+
pushModels(tcgcField.type, container);
|
|
1639
2064
|
}
|
|
1640
2065
|
};
|
|
1641
|
-
|
|
2066
|
+
const format = responseFormat === 'JsonFormat' || responseFormat === 'XmlFormat' ? responseFormat : 'JsonFormat';
|
|
2067
|
+
const statusModel = this.getModel(method.lroMetadata.pollingInfo.responseModel, undefined, `${rustClient.name}${utils.pascalCase(rustMethod.name, false)}OperationStatus`);
|
|
2068
|
+
const statusType = this.typeToWireType(statusModel);
|
|
2069
|
+
if (statusType.kind !== 'model') {
|
|
2070
|
+
throw new AdapterError('InternalError', `status type for an LRO method '${method.name}' is not a model`, (_q = method.__raw) === null || _q === void 0 ? void 0 : _q.node);
|
|
2071
|
+
}
|
|
2072
|
+
// the adapted type likely comes from core which means
|
|
2073
|
+
// it will be in the root namespace. since we emit these
|
|
2074
|
+
// per client.operation, set the correct module
|
|
2075
|
+
statusType.module = rustClient.module;
|
|
2076
|
+
pushModels(method.lroMetadata.pollingInfo.responseModel, rustClient.module, statusModel, false);
|
|
1642
2077
|
const poller = new rust.Poller(this.crate, new rust.Response(this.crate, statusType, format));
|
|
1643
|
-
if (
|
|
1644
|
-
const
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
}
|
|
1648
|
-
pushModels(responseType, this.crate);
|
|
1649
|
-
poller.resultType = new rust.Response(this.crate, responseType, format);
|
|
2078
|
+
if (method.response.type) {
|
|
2079
|
+
const resultType = this.getType(method.response.type);
|
|
2080
|
+
pushModels(method.response.type, rustClient.module, resultType);
|
|
2081
|
+
poller.resultType = new rust.Response(this.crate, this.typeToWireType(resultType), format);
|
|
1650
2082
|
}
|
|
1651
2083
|
rustMethod.returns = new rust.Result(this.crate, poller);
|
|
1652
2084
|
}
|
|
1653
|
-
else if (method.response.type &&
|
|
2085
|
+
else if (method.response.type && responseFormat !== 'BinaryFormat') {
|
|
1654
2086
|
const response = new rust.Response(this.crate, this.typeToWireType(this.getType(method.response.type)), responseFormat);
|
|
1655
2087
|
rustMethod.returns = new rust.Result(this.crate, response);
|
|
1656
2088
|
}
|
|
@@ -1659,9 +2091,9 @@ export class Adapter {
|
|
|
1659
2091
|
// we need to return a marker type
|
|
1660
2092
|
const markerType = new rust.MarkerType(`${rustClient.name}${utils.pascalCase(method.name, false)}Result`, rustMethod.visibility);
|
|
1661
2093
|
markerType.docs.summary = `Contains results for ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
|
|
1662
|
-
|
|
2094
|
+
rustClient.module.models.push(markerType);
|
|
1663
2095
|
let resultType;
|
|
1664
|
-
if (
|
|
2096
|
+
if (responseFormat === 'BinaryFormat') {
|
|
1665
2097
|
// method returns a streaming binary response with headers
|
|
1666
2098
|
resultType = new rust.AsyncResponse(this.crate, markerType);
|
|
1667
2099
|
}
|
|
@@ -1670,8 +2102,8 @@ export class Adapter {
|
|
|
1670
2102
|
}
|
|
1671
2103
|
rustMethod.returns = new rust.Result(this.crate, resultType);
|
|
1672
2104
|
}
|
|
1673
|
-
else if (
|
|
1674
|
-
//
|
|
2105
|
+
else if (responseFormat === 'BinaryFormat') {
|
|
2106
|
+
// binary format indicates a streaming binary response
|
|
1675
2107
|
rustMethod.returns = new rust.Result(this.crate, new rust.AsyncResponse(this.crate, this.getUnitType()));
|
|
1676
2108
|
}
|
|
1677
2109
|
else {
|
|
@@ -1692,13 +2124,13 @@ export class Adapter {
|
|
|
1692
2124
|
// can't do this until the method has been completely adapted
|
|
1693
2125
|
const pageableMethod = rustMethod;
|
|
1694
2126
|
pageableMethod.strategy = this.adaptPageableMethodStrategy(method, paramsMap, responseHeadersMap);
|
|
1695
|
-
if (((
|
|
2127
|
+
if (((_r = pageableMethod.strategy) === null || _r === void 0 ? void 0 : _r.kind) === 'nextLink') {
|
|
1696
2128
|
pageableMethod.strategy.reinjectedParams = this.adaptPageableMethodReinjectionParams(method, paramsMap);
|
|
1697
2129
|
}
|
|
1698
|
-
else if (((
|
|
2130
|
+
else if (((_s = pageableMethod.strategy) === null || _s === void 0 ? void 0 : _s.kind) === 'continuationToken') {
|
|
1699
2131
|
// set the continuation type to token on the Pager and the PagerOptions field in the method options
|
|
1700
2132
|
pageableMethod.returns.type.continuation = 'token';
|
|
1701
|
-
for (const field of pageableMethod.options.type.fields) {
|
|
2133
|
+
for (const field of pageableMethod.options.type.type.fields) {
|
|
1702
2134
|
if (field.type.kind === 'pagerOptions') {
|
|
1703
2135
|
field.type.continuation = 'token';
|
|
1704
2136
|
}
|
|
@@ -1775,14 +2207,6 @@ export class Adapter {
|
|
|
1775
2207
|
return utils.capitalize(type.kind);
|
|
1776
2208
|
}
|
|
1777
2209
|
};
|
|
1778
|
-
const throwOnUnitType = function (type) {
|
|
1779
|
-
if (type.kind === 'unit') {
|
|
1780
|
-
// this is to filter out unit from ResponseTypes.
|
|
1781
|
-
// methods that return unit should have been skipped by our caller
|
|
1782
|
-
// so we should never hit this (if we do, we have a bug elsewhere).
|
|
1783
|
-
throw new AdapterError('InternalError', `unexpected trait impl kind ${type.kind}`);
|
|
1784
|
-
}
|
|
1785
|
-
};
|
|
1786
2210
|
// response header traits are only ever for marker types and payloads
|
|
1787
2211
|
let implFor;
|
|
1788
2212
|
switch (method.returns.type.kind) {
|
|
@@ -1791,18 +2215,29 @@ export class Adapter {
|
|
|
1791
2215
|
implFor = method.returns.type.type;
|
|
1792
2216
|
break;
|
|
1793
2217
|
case 'response':
|
|
1794
|
-
|
|
1795
|
-
|
|
2218
|
+
switch (method.returns.type.content.kind) {
|
|
2219
|
+
case 'marker':
|
|
2220
|
+
case 'model':
|
|
2221
|
+
implFor = method.returns.type;
|
|
2222
|
+
break;
|
|
2223
|
+
default:
|
|
2224
|
+
throw new AdapterError('InternalError', `unexpected trait impl content kind ${method.returns.type.content.kind}`);
|
|
2225
|
+
}
|
|
1796
2226
|
break;
|
|
1797
2227
|
case 'asyncResponse':
|
|
1798
|
-
|
|
1799
|
-
|
|
2228
|
+
switch (method.returns.type.type.kind) {
|
|
2229
|
+
case 'marker':
|
|
2230
|
+
implFor = method.returns.type;
|
|
2231
|
+
break;
|
|
2232
|
+
default:
|
|
2233
|
+
throw new AdapterError('InternalError', `unexpected trait impl type kind ${method.returns.type.type.kind}`);
|
|
2234
|
+
}
|
|
1800
2235
|
break;
|
|
1801
2236
|
}
|
|
1802
2237
|
const traitName = `${recursiveTypeName(implFor.kind === 'asyncResponse' ? implFor.type : implFor.content)}Headers`;
|
|
1803
2238
|
// NOTE: the complete doc text will be emitted at codegen time
|
|
1804
2239
|
const docs = this.asDocLink(`${client.name}::${method.name}()`, `crate::generated::clients::${client.name}::${method.name}()`);
|
|
1805
|
-
const responseHeadersTrait = new rust.ResponseHeadersTrait(traitName, implFor, docs, method.visibility);
|
|
2240
|
+
const responseHeadersTrait = new rust.ResponseHeadersTrait(traitName, implFor, docs, method.visibility, client.module);
|
|
1806
2241
|
responseHeadersTrait.headers.push(...responseHeaders);
|
|
1807
2242
|
return responseHeadersTrait;
|
|
1808
2243
|
}
|
|
@@ -1922,12 +2357,18 @@ export class Adapter {
|
|
|
1922
2357
|
return paramsForReinjection;
|
|
1923
2358
|
}
|
|
1924
2359
|
/**
|
|
1925
|
-
* converts a tcgc operation parameter into a Rust method parameter
|
|
2360
|
+
* converts a tcgc operation parameter into a Rust method parameter.
|
|
2361
|
+
* note that when methodParam is present, we must use all applicable
|
|
2362
|
+
* values from methodParam as the source of truth. e.g. when overriding
|
|
2363
|
+
* a method to make an optional param required, the requiredness will
|
|
2364
|
+
* be reflected in the method param, _not_ the operation param.
|
|
1926
2365
|
*
|
|
1927
|
-
* @param
|
|
2366
|
+
* @param opParam the tcgc operation parameter to convert
|
|
2367
|
+
* @param methodParam the tcgc method parameter associated with opParam
|
|
2368
|
+
* @param lifetime optional lifetime to stamp on borrowed ref types (used for grouped params that are struct fields)
|
|
1928
2369
|
* @returns a Rust method parameter
|
|
1929
2370
|
*/
|
|
1930
|
-
adaptMethodParameter(
|
|
2371
|
+
adaptMethodParameter(opParam, methodParam, lifetime) {
|
|
1931
2372
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1932
2373
|
/**
|
|
1933
2374
|
* used to create keys for this.clientMethodParams
|
|
@@ -1939,10 +2380,10 @@ export class Adapter {
|
|
|
1939
2380
|
// in different places across methods (path/query)
|
|
1940
2381
|
return `${param.name}-${param.kind}`;
|
|
1941
2382
|
};
|
|
1942
|
-
const paramLoc =
|
|
2383
|
+
const paramLoc = opParam.onClient ? 'client' : 'method';
|
|
1943
2384
|
// if this is a client method param, check if we've already adapted it
|
|
1944
2385
|
if (paramLoc === 'client') {
|
|
1945
|
-
const clientMethodParam = this.clientMethodParams.get(getClientParamsKey(
|
|
2386
|
+
const clientMethodParam = this.clientMethodParams.get(getClientParamsKey(opParam));
|
|
1946
2387
|
if (clientMethodParam) {
|
|
1947
2388
|
return clientMethodParam;
|
|
1948
2389
|
}
|
|
@@ -1958,40 +2399,43 @@ export class Adapter {
|
|
|
1958
2399
|
}
|
|
1959
2400
|
return param.name;
|
|
1960
2401
|
};
|
|
1961
|
-
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(getCorrespondingClientParamName(
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
2402
|
+
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(getCorrespondingClientParamName(opParam)), 'param', reservedParams);
|
|
2403
|
+
const paramOptional = methodParam ? methodParam.optional : opParam.optional;
|
|
2404
|
+
let paramType = this.getType(methodParam ? methodParam.type : opParam.type);
|
|
2405
|
+
// for required header/path/query method string params, we might emit them as borrowed types.
|
|
2406
|
+
if (!paramOptional && paramLoc !== 'client' && (opParam.kind === 'header' || opParam.kind === 'path' || opParam.kind === 'query')) {
|
|
2407
|
+
const borrowedType = this.canBorrowMethodParam(paramType, opParam.kind, lifetime);
|
|
1966
2408
|
if (borrowedType) {
|
|
1967
2409
|
paramType = borrowedType;
|
|
1968
2410
|
}
|
|
1969
2411
|
}
|
|
1970
2412
|
let adaptedParam;
|
|
1971
|
-
switch (
|
|
2413
|
+
switch (opParam.kind) {
|
|
1972
2414
|
case 'body': {
|
|
1973
2415
|
let requestType;
|
|
1974
|
-
|
|
1975
|
-
|
|
2416
|
+
const requestFormatType = this.getPayloadFormatType(opParam.type, opParam.defaultContentType);
|
|
2417
|
+
if (requestFormatType === 'BinaryFormat') {
|
|
2418
|
+
// binary format indicates a streaming binary request
|
|
1976
2419
|
requestType = new rust.Bytes(this.crate);
|
|
1977
2420
|
}
|
|
1978
2421
|
else {
|
|
1979
|
-
requestType =
|
|
2422
|
+
requestType = this.typeToWireType(paramType);
|
|
1980
2423
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
2424
|
+
// binary payloads use NoFormat as the body is sent as raw bytes
|
|
2425
|
+
const wireFormat = requestFormatType === 'BinaryFormat' ? 'NoFormat' : requestFormatType;
|
|
2426
|
+
adaptedParam = new rust.BodyParameter(paramName, paramLoc, paramOptional, new rust.RequestContent(this.crate, requestType, wireFormat));
|
|
1983
2427
|
break;
|
|
1984
2428
|
}
|
|
1985
2429
|
case 'cookie':
|
|
1986
2430
|
// TODO: https://github.com/Azure/typespec-rust/issues/192
|
|
1987
|
-
throw new AdapterError('UnsupportedTsp', 'cookie parameters are not supported', (_a =
|
|
2431
|
+
throw new AdapterError('UnsupportedTsp', 'cookie parameters are not supported', (_a = opParam.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
1988
2432
|
case 'header':
|
|
1989
|
-
if (
|
|
2433
|
+
if (opParam.collectionFormat) {
|
|
1990
2434
|
if (paramType.kind !== 'Vec' && !isRefSlice(paramType)) {
|
|
1991
|
-
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for HeaderCollectionParameter`, (_b =
|
|
2435
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for HeaderCollectionParameter`, (_b = opParam.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
1992
2436
|
}
|
|
1993
2437
|
let format;
|
|
1994
|
-
switch (
|
|
2438
|
+
switch (opParam.collectionFormat) {
|
|
1995
2439
|
case 'csv':
|
|
1996
2440
|
case 'simple':
|
|
1997
2441
|
format = 'csv';
|
|
@@ -1999,18 +2443,18 @@ export class Adapter {
|
|
|
1999
2443
|
case 'pipes':
|
|
2000
2444
|
case 'ssv':
|
|
2001
2445
|
case 'tsv':
|
|
2002
|
-
format =
|
|
2446
|
+
format = opParam.collectionFormat;
|
|
2003
2447
|
break;
|
|
2004
2448
|
default:
|
|
2005
|
-
throw new AdapterError('InternalError', `unexpected format ${
|
|
2449
|
+
throw new AdapterError('InternalError', `unexpected format ${opParam.collectionFormat} for HeaderCollectionParameter`, (_c = opParam.__raw) === null || _c === void 0 ? void 0 : _c.node);
|
|
2006
2450
|
}
|
|
2007
|
-
adaptedParam = new rust.HeaderCollectionParameter(paramName,
|
|
2451
|
+
adaptedParam = new rust.HeaderCollectionParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType, format);
|
|
2008
2452
|
}
|
|
2009
|
-
else if (
|
|
2453
|
+
else if (opParam.serializedName === 'x-ms-meta') {
|
|
2010
2454
|
if (paramType.kind !== 'hashmap' && !isRefHashMap(paramType)) {
|
|
2011
|
-
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for header ${
|
|
2455
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for header ${opParam.serializedName}`, (_d = opParam.__raw) === null || _d === void 0 ? void 0 : _d.node);
|
|
2012
2456
|
}
|
|
2013
|
-
adaptedParam = new rust.HeaderHashMapParameter(paramName,
|
|
2457
|
+
adaptedParam = new rust.HeaderHashMapParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType);
|
|
2014
2458
|
}
|
|
2015
2459
|
else {
|
|
2016
2460
|
paramType = this.typeToWireType(paramType);
|
|
@@ -2021,28 +2465,28 @@ export class Adapter {
|
|
|
2021
2465
|
case 'slice':
|
|
2022
2466
|
case 'str':
|
|
2023
2467
|
case 'Vec':
|
|
2024
|
-
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for scalar header ${
|
|
2468
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for scalar header ${opParam.serializedName}`, (_e = opParam.__raw) === null || _e === void 0 ? void 0 : _e.node);
|
|
2025
2469
|
}
|
|
2026
|
-
adaptedParam = new rust.HeaderScalarParameter(paramName,
|
|
2027
|
-
adaptedParam.isApiVersion =
|
|
2470
|
+
adaptedParam = new rust.HeaderScalarParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType);
|
|
2471
|
+
adaptedParam.isApiVersion = opParam.isApiVersionParam;
|
|
2028
2472
|
}
|
|
2029
2473
|
break;
|
|
2030
2474
|
case 'path':
|
|
2031
2475
|
{
|
|
2032
2476
|
paramType = this.typeToWireType(paramType);
|
|
2033
2477
|
let style = 'simple';
|
|
2034
|
-
const tspStyleString =
|
|
2478
|
+
const tspStyleString = opParam.style;
|
|
2035
2479
|
if (!['simple', 'path', 'label', 'matrix'].includes(tspStyleString)) {
|
|
2036
|
-
throw new AdapterError('InternalError', `unsupported style ${tspStyleString} for parameter ${
|
|
2480
|
+
throw new AdapterError('InternalError', `unsupported style ${tspStyleString} for parameter ${opParam.serializedName}`, (_f = opParam.__raw) === null || _f === void 0 ? void 0 : _f.node);
|
|
2037
2481
|
}
|
|
2038
2482
|
else {
|
|
2039
2483
|
style = tspStyleString;
|
|
2040
2484
|
}
|
|
2041
2485
|
if (isRefSlice(paramType)) {
|
|
2042
|
-
adaptedParam = new rust.PathCollectionParameter(paramName,
|
|
2486
|
+
adaptedParam = new rust.PathCollectionParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType, opParam.allowReserved, style, opParam.explode);
|
|
2043
2487
|
}
|
|
2044
2488
|
else if (paramType.kind === 'hashmap' || isRefHashMap(paramType)) {
|
|
2045
|
-
adaptedParam = new rust.PathHashMapParameter(paramName,
|
|
2489
|
+
adaptedParam = new rust.PathHashMapParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType, opParam.allowReserved, style, opParam.explode);
|
|
2046
2490
|
}
|
|
2047
2491
|
else {
|
|
2048
2492
|
switch (paramType.kind) {
|
|
@@ -2051,25 +2495,25 @@ export class Adapter {
|
|
|
2051
2495
|
case 'slice':
|
|
2052
2496
|
case 'str':
|
|
2053
2497
|
case 'Vec':
|
|
2054
|
-
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for scalar path ${
|
|
2498
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for scalar path ${opParam.serializedName}`, (_g = opParam.__raw) === null || _g === void 0 ? void 0 : _g.node);
|
|
2055
2499
|
}
|
|
2056
|
-
adaptedParam = new rust.PathScalarParameter(paramName,
|
|
2500
|
+
adaptedParam = new rust.PathScalarParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType, opParam.allowReserved, style);
|
|
2057
2501
|
}
|
|
2058
2502
|
}
|
|
2059
2503
|
break;
|
|
2060
2504
|
case 'query':
|
|
2061
2505
|
paramType = this.typeToWireType(paramType);
|
|
2062
2506
|
if (paramType.kind === 'Vec' || isRefSlice(paramType)) {
|
|
2063
|
-
let format =
|
|
2064
|
-
if (
|
|
2065
|
-
format =
|
|
2507
|
+
let format = opParam.explode ? 'multi' : 'csv';
|
|
2508
|
+
if (opParam.collectionFormat) {
|
|
2509
|
+
format = opParam.collectionFormat === 'simple' ? 'csv' : (opParam.collectionFormat === 'form' ? 'multi' : opParam.collectionFormat);
|
|
2066
2510
|
}
|
|
2067
2511
|
// TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
|
|
2068
|
-
adaptedParam = new rust.QueryCollectionParameter(paramName,
|
|
2512
|
+
adaptedParam = new rust.QueryCollectionParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType, true, format);
|
|
2069
2513
|
}
|
|
2070
2514
|
else if (paramType.kind === 'hashmap' || isRefHashMap(paramType)) {
|
|
2071
2515
|
// TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
|
|
2072
|
-
adaptedParam = new rust.QueryHashMapParameter(paramName,
|
|
2516
|
+
adaptedParam = new rust.QueryHashMapParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType, true, opParam.explode);
|
|
2073
2517
|
}
|
|
2074
2518
|
else {
|
|
2075
2519
|
switch (paramType.kind) {
|
|
@@ -2077,17 +2521,17 @@ export class Adapter {
|
|
|
2077
2521
|
case 'model':
|
|
2078
2522
|
case 'slice':
|
|
2079
2523
|
case 'str':
|
|
2080
|
-
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for scalar query ${
|
|
2524
|
+
throw new AdapterError('InternalError', `unexpected kind ${paramType.kind} for scalar query ${opParam.serializedName}`, (_h = opParam.__raw) === null || _h === void 0 ? void 0 : _h.node);
|
|
2081
2525
|
}
|
|
2082
2526
|
// TODO: hard-coded encoding setting, https://github.com/Azure/typespec-azure/issues/1314
|
|
2083
|
-
adaptedParam = new rust.QueryScalarParameter(paramName,
|
|
2084
|
-
adaptedParam.isApiVersion =
|
|
2527
|
+
adaptedParam = new rust.QueryScalarParameter(paramName, opParam.serializedName, paramLoc, paramOptional, paramType, true);
|
|
2528
|
+
adaptedParam.isApiVersion = opParam.isApiVersionParam;
|
|
2085
2529
|
}
|
|
2086
2530
|
break;
|
|
2087
2531
|
}
|
|
2088
|
-
adaptedParam.docs = this.adaptDocs(
|
|
2532
|
+
adaptedParam.docs = this.adaptDocs(methodParam ? methodParam.summary : opParam.summary, methodParam ? methodParam.doc : opParam.doc);
|
|
2089
2533
|
if (paramLoc === 'client') {
|
|
2090
|
-
this.clientMethodParams.set(getClientParamsKey(
|
|
2534
|
+
this.clientMethodParams.set(getClientParamsKey(opParam), adaptedParam);
|
|
2091
2535
|
}
|
|
2092
2536
|
return adaptedParam;
|
|
2093
2537
|
}
|
|
@@ -2099,17 +2543,18 @@ export class Adapter {
|
|
|
2099
2543
|
*
|
|
2100
2544
|
* @param type the param type to be updated
|
|
2101
2545
|
* @param kind the kind of param
|
|
2546
|
+
* @param lifetime optional lifetime to stamp on Ref types (used for struct fields)
|
|
2102
2547
|
* @returns the updated param type or undefined
|
|
2103
2548
|
*/
|
|
2104
|
-
canBorrowMethodParam(type, kind) {
|
|
2549
|
+
canBorrowMethodParam(type, kind, lifetime) {
|
|
2105
2550
|
const recursiveBuildVecStr = (v) => {
|
|
2106
2551
|
switch (v.kind) {
|
|
2107
2552
|
case 'encodedBytes':
|
|
2108
|
-
return this.getRefType(this.getEncodedBytes(v.encoding, true));
|
|
2553
|
+
return this.getRefType(this.getEncodedBytes(v.encoding, true), lifetime);
|
|
2109
2554
|
case 'hashmap':
|
|
2110
2555
|
return this.getHashMap(this.typeToWireType(recursiveBuildVecStr(v.type)));
|
|
2111
2556
|
case 'String':
|
|
2112
|
-
return this.getRefType(this.getStringSlice());
|
|
2557
|
+
return this.getRefType(this.getStringSlice(), lifetime);
|
|
2113
2558
|
case 'Vec':
|
|
2114
2559
|
return this.getVec(this.typeToWireType(recursiveBuildVecStr(v.type)));
|
|
2115
2560
|
default:
|
|
@@ -2126,19 +2571,19 @@ export class Adapter {
|
|
|
2126
2571
|
case 'String':
|
|
2127
2572
|
// header String params are always owned
|
|
2128
2573
|
if (kind !== 'header') {
|
|
2129
|
-
return this.getRefType(this.getStringSlice());
|
|
2574
|
+
return this.getRefType(this.getStringSlice(), lifetime);
|
|
2130
2575
|
}
|
|
2131
2576
|
break;
|
|
2132
2577
|
case 'Vec': {
|
|
2133
2578
|
// if this is an array of string, we ultimately want a slice of &str
|
|
2134
2579
|
const unwrapped = recursiveUnwrapVec(type);
|
|
2135
2580
|
if (unwrapped.kind === 'String' || unwrapped.kind === 'encodedBytes') {
|
|
2136
|
-
return this.getRefType(this.getSlice(recursiveBuildVecStr(type.type)));
|
|
2581
|
+
return this.getRefType(this.getSlice(recursiveBuildVecStr(type.type)), lifetime);
|
|
2137
2582
|
}
|
|
2138
|
-
return this.getRefType(this.getSlice(type.type));
|
|
2583
|
+
return this.getRefType(this.getSlice(type.type), lifetime);
|
|
2139
2584
|
}
|
|
2140
2585
|
case 'encodedBytes':
|
|
2141
|
-
return this.getRefType(this.getEncodedBytes(type.encoding, true));
|
|
2586
|
+
return this.getRefType(this.getEncodedBytes(type.encoding, true), lifetime);
|
|
2142
2587
|
case 'decimal':
|
|
2143
2588
|
case 'Etag':
|
|
2144
2589
|
case 'hashmap':
|
|
@@ -2148,7 +2593,7 @@ export class Adapter {
|
|
|
2148
2593
|
case 'Url':
|
|
2149
2594
|
// these types all require conversion
|
|
2150
2595
|
// to String so we don't need to own them
|
|
2151
|
-
return this.getRefType(type);
|
|
2596
|
+
return this.getRefType(type, lifetime);
|
|
2152
2597
|
}
|
|
2153
2598
|
return undefined;
|
|
2154
2599
|
}
|
|
@@ -2180,6 +2625,7 @@ export class Adapter {
|
|
|
2180
2625
|
case 'slice':
|
|
2181
2626
|
case 'str':
|
|
2182
2627
|
case 'String':
|
|
2628
|
+
case 'untaggedUnion':
|
|
2183
2629
|
case 'Url':
|
|
2184
2630
|
case 'Vec':
|
|
2185
2631
|
return type;
|
|
@@ -2215,46 +2661,41 @@ export class Adapter {
|
|
|
2215
2661
|
}
|
|
2216
2662
|
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(param.name), 'param');
|
|
2217
2663
|
const paramLoc = 'method';
|
|
2218
|
-
const
|
|
2219
|
-
const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, this.getType(param.type), new rust.RequestContent(this.crate, new rust.Payload(payloadType, format), formatType));
|
|
2664
|
+
const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, this.getType(param.type), new rust.RequestContent(this.crate, payloadType, format));
|
|
2220
2665
|
return adaptedParam;
|
|
2221
2666
|
}
|
|
2222
2667
|
/**
|
|
2223
|
-
*
|
|
2668
|
+
* determines the payload format type from serializationOptions on the type, falling
|
|
2669
|
+
* back to inspecting the defaultContentType when serializationOptions isn't available.
|
|
2224
2670
|
*
|
|
2225
|
-
* @param
|
|
2226
|
-
* @
|
|
2671
|
+
* @param type the SDK type associated with the payload, if available
|
|
2672
|
+
* @param defaultContentType the value of the Accept or Content-Type header
|
|
2673
|
+
* @returns a payload format type
|
|
2227
2674
|
*/
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2675
|
+
getPayloadFormatType(type, defaultContentType) {
|
|
2676
|
+
if ((type === null || type === void 0 ? void 0 : type.kind) === 'model') {
|
|
2677
|
+
const opts = type.serializationOptions;
|
|
2678
|
+
if (opts.json) {
|
|
2679
|
+
return 'JsonFormat';
|
|
2680
|
+
}
|
|
2681
|
+
else if (opts.xml) {
|
|
2682
|
+
this.crate.addDependency(new rust.CrateDependency('azure_core', ['xml']));
|
|
2683
|
+
return 'XmlFormat';
|
|
2684
|
+
}
|
|
2685
|
+
else if (opts.binary) {
|
|
2686
|
+
return 'BinaryFormat';
|
|
2687
|
+
}
|
|
2240
2688
|
}
|
|
2241
|
-
else {
|
|
2242
|
-
|
|
2689
|
+
else if ((type === null || type === void 0 ? void 0 : type.kind) === 'bytes' && type.encode === 'bytes') {
|
|
2690
|
+
// fallback: check if it's a bytes type with bytes encoding (binary stream)
|
|
2691
|
+
return 'BinaryFormat';
|
|
2243
2692
|
}
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
*
|
|
2248
|
-
* @param contentType the value of the Accept or Content-Type header
|
|
2249
|
-
* @returns a response format
|
|
2250
|
-
*/
|
|
2251
|
-
adaptPayloadFormatType(contentType) {
|
|
2252
|
-
// we only recognize/support JSON and XML content types.
|
|
2253
|
-
// anything else is NoFormat
|
|
2254
|
-
if (contentType.match(/json/i)) {
|
|
2693
|
+
// tcgc doesn't yet have serializationOptions on types other
|
|
2694
|
+
// than models. for those cases, fall back to the header value
|
|
2695
|
+
if (defaultContentType.match(/json/i)) {
|
|
2255
2696
|
return 'JsonFormat';
|
|
2256
2697
|
}
|
|
2257
|
-
else if (
|
|
2698
|
+
else if (defaultContentType.match(/xml/i)) {
|
|
2258
2699
|
// XML support is disabled by default
|
|
2259
2700
|
this.crate.addDependency(new rust.CrateDependency('azure_core', ['xml']));
|
|
2260
2701
|
return 'XmlFormat';
|
|
@@ -2303,15 +2744,14 @@ function recursiveKeyName(root, type) {
|
|
|
2303
2744
|
case 'discriminatedUnion':
|
|
2304
2745
|
case 'model':
|
|
2305
2746
|
case 'struct':
|
|
2747
|
+
case 'untaggedUnion':
|
|
2306
2748
|
return `${root}-${type.kind}-${type.name}`;
|
|
2307
2749
|
case 'literal':
|
|
2308
2750
|
return `${recursiveKeyName(`${root}-${type.kind}`, type.valueKind)}-${type.value}`;
|
|
2309
|
-
case 'payload':
|
|
2310
|
-
return recursiveKeyName(`${root}-${type.kind}-${type.format}`, type.type);
|
|
2311
2751
|
case 'ref':
|
|
2312
|
-
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
2752
|
+
return recursiveKeyName(`${root}-${type.kind}${type.lifetime ? `-${type.lifetime.name}` : ''}`, type.type);
|
|
2313
2753
|
case 'requestContent':
|
|
2314
|
-
return recursiveKeyName(`${root}-${type.kind}`, type.content);
|
|
2754
|
+
return recursiveKeyName(`${root}-${type.kind}-${type.format}`, type.content);
|
|
2315
2755
|
case 'safeint':
|
|
2316
2756
|
case 'decimal':
|
|
2317
2757
|
return `${root}-${type.kind}${type.stringEncoding ? '-string' : ''}`;
|
|
@@ -2401,9 +2841,67 @@ function isHttpStatusCodeRange(statusCode) {
|
|
|
2401
2841
|
function adaptAccessFlags(access) {
|
|
2402
2842
|
return access === 'public' ? 'pub' : 'pubCrate';
|
|
2403
2843
|
}
|
|
2844
|
+
/**
|
|
2845
|
+
* converts an array of Visibility flags to a sorted, human-readable string.
|
|
2846
|
+
* returns undefined if visibility is unrestricted (all flags or undefined).
|
|
2847
|
+
*
|
|
2848
|
+
* @param visibility the visibility flags from TCGC
|
|
2849
|
+
* @returns a formatted string like "Read" or "Create, Update", or undefined if unrestricted
|
|
2850
|
+
*/
|
|
2851
|
+
export function formatVisibility(visibility) {
|
|
2852
|
+
if (!visibility || visibility.length === 0) {
|
|
2853
|
+
return undefined;
|
|
2854
|
+
}
|
|
2855
|
+
// combine all flags into a single bitmask
|
|
2856
|
+
let combined = 0;
|
|
2857
|
+
for (const v of visibility) {
|
|
2858
|
+
combined |= v;
|
|
2859
|
+
}
|
|
2860
|
+
// if all lifecycle flags are set, there's no restriction
|
|
2861
|
+
if ((combined & http.Visibility.All) === http.Visibility.All) {
|
|
2862
|
+
return undefined;
|
|
2863
|
+
}
|
|
2864
|
+
// flags are checked in sorted order: Create, Delete, Query, Read, Update
|
|
2865
|
+
const names = [];
|
|
2866
|
+
if (combined & http.Visibility.Create) {
|
|
2867
|
+
names.push('Create');
|
|
2868
|
+
}
|
|
2869
|
+
if (combined & http.Visibility.Delete) {
|
|
2870
|
+
names.push('Delete');
|
|
2871
|
+
}
|
|
2872
|
+
if (combined & http.Visibility.Query) {
|
|
2873
|
+
names.push('Query');
|
|
2874
|
+
}
|
|
2875
|
+
if (combined & http.Visibility.Read) {
|
|
2876
|
+
names.push('Read');
|
|
2877
|
+
}
|
|
2878
|
+
if (combined & http.Visibility.Update) {
|
|
2879
|
+
names.push('Update');
|
|
2880
|
+
}
|
|
2881
|
+
return names.length > 0 ? names.join(', ') : undefined;
|
|
2882
|
+
}
|
|
2404
2883
|
/** contains reserved param names */
|
|
2405
2884
|
const reservedParams = new Set([
|
|
2406
2885
|
// reserved per SDK guidelines
|
|
2407
2886
|
'options',
|
|
2408
2887
|
]);
|
|
2888
|
+
/**
|
|
2889
|
+
* possible namespaces from core libraries.
|
|
2890
|
+
* all types from these namespaces will be placed
|
|
2891
|
+
* into the root namespace.
|
|
2892
|
+
* if more core libs show up, add their namespaces here.
|
|
2893
|
+
*/
|
|
2894
|
+
const LIB_NAMESPACE = [
|
|
2895
|
+
"azure.clientgenerator.core",
|
|
2896
|
+
"azure.core",
|
|
2897
|
+
"azure.resourcemanager",
|
|
2898
|
+
"typespec.http",
|
|
2899
|
+
"typespec.rest",
|
|
2900
|
+
"typespec.versioning",
|
|
2901
|
+
];
|
|
2902
|
+
/** returns true if the given namespace is a known library namespace or a child of one */
|
|
2903
|
+
function isLibNamespace(namespace) {
|
|
2904
|
+
const ns = namespace.toLowerCase();
|
|
2905
|
+
return LIB_NAMESPACE.some((lib) => ns === lib || ns.startsWith(lib + '.'));
|
|
2906
|
+
}
|
|
2409
2907
|
//# sourceMappingURL=adapter.js.map
|