@azure-tools/typespec-rust 0.36.0 → 0.38.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 +208 -88
- 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 +57 -15
- 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 -3
- 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 +22 -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 +96 -39
- 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 +81 -62
- 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 +32 -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 +69 -38
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +31 -10
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/emitter.d.ts.map +1 -1
- package/dist/src/emitter.js +40 -12
- 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 +76 -13
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +777 -190
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/dist/src/utils/utils.d.ts +23 -4
- package/dist/src/utils/utils.d.ts.map +1 -1
- package/dist/src/utils/utils.js +54 -23
- package/dist/src/utils/utils.js.map +1 -1
- package/package.json +17 -17
|
@@ -2,15 +2,15 @@
|
|
|
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 requiredness 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';
|
|
10
11
|
import * as tcgc from '@azure-tools/typespec-client-generator-core';
|
|
11
12
|
import * as rust from '../codemodel/index.js';
|
|
12
13
|
import { FinalStateValue } from "@azure-tools/typespec-azure-core";
|
|
13
|
-
import { NoTarget } from "@typespec/compiler";
|
|
14
14
|
/**
|
|
15
15
|
* AdapterError is thrown when the emitter fails to convert part of the tcgc code
|
|
16
16
|
* model to the emitter code model. this could be due to the emitter not supporting
|
|
@@ -58,11 +58,32 @@ export class Adapter {
|
|
|
58
58
|
return new Adapter(ctx, context.options);
|
|
59
59
|
}
|
|
60
60
|
constructor(ctx, options) {
|
|
61
|
+
this.rootNamespace = '';
|
|
61
62
|
this.types = new Map();
|
|
62
63
|
this.clientMethodParams = new Map();
|
|
63
64
|
this.fieldsMap = new Map();
|
|
64
65
|
this.ctx = ctx;
|
|
65
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
|
+
}
|
|
66
87
|
let serviceType = 'data-plane';
|
|
67
88
|
if (this.ctx.arm === true) {
|
|
68
89
|
serviceType = 'azure-arm';
|
|
@@ -70,17 +91,9 @@ export class Adapter {
|
|
|
70
91
|
this.crate = new rust.Crate(this.options['crate-name'], this.options['crate-version'], serviceType);
|
|
71
92
|
}
|
|
72
93
|
/** performs all the steps to convert tcgc to a crate */
|
|
73
|
-
tcgcToCrate(
|
|
74
|
-
this.adaptTypes(
|
|
94
|
+
tcgcToCrate() {
|
|
95
|
+
this.adaptTypes();
|
|
75
96
|
this.adaptClients();
|
|
76
|
-
// marker models don't require serde so exclude them from the check
|
|
77
|
-
if (this.crate.enums.length > 0 || this.crate.models.some(e => e.kind === 'model')) {
|
|
78
|
-
this.crate.addDependency(new rust.CrateDependency('serde'));
|
|
79
|
-
}
|
|
80
|
-
if (this.crate.clients.length > 0 || this.crate.enums.length > 0 || this.crate.models.length > 0 || this.crate.unions.length > 0) {
|
|
81
|
-
this.crate.addDependency(new rust.CrateDependency('azure_core'));
|
|
82
|
-
}
|
|
83
|
-
this.crate.sortContent();
|
|
84
97
|
return this.crate;
|
|
85
98
|
}
|
|
86
99
|
/** converts tcgc docs to formatted rust.Docs */
|
|
@@ -96,21 +109,78 @@ export class Adapter {
|
|
|
96
109
|
description: doc,
|
|
97
110
|
};
|
|
98
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
|
+
}
|
|
99
171
|
/** converts all tcgc types to their Rust type equivalent */
|
|
100
|
-
adaptTypes(
|
|
172
|
+
adaptTypes() {
|
|
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
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
severity: 'warning',
|
|
107
|
-
message: `Non-discriminated unions ('${sdkUnion.name}') are not supported. No type will be generated.`,
|
|
108
|
-
target: NoTarget,
|
|
109
|
-
});
|
|
176
|
+
// getNonDiscriminatedUnion() self-registers the type into the module and caches it
|
|
177
|
+
this.getNonDiscriminatedUnion(sdkUnion);
|
|
178
|
+
needsCoreAndSerde = true;
|
|
110
179
|
continue;
|
|
111
180
|
}
|
|
112
181
|
const rustUnion = this.getDiscriminatedUnion(sdkUnion);
|
|
113
|
-
this.
|
|
182
|
+
this.adaptNamespace(sdkUnion.namespace).unions.push(rustUnion);
|
|
183
|
+
needsCoreAndSerde = true;
|
|
114
184
|
}
|
|
115
185
|
for (const sdkEnum of this.ctx.sdkPackage.enums) {
|
|
116
186
|
if ((sdkEnum.usage & tcgc.UsageFlags.ApiVersionEnum) === tcgc.UsageFlags.ApiVersionEnum) {
|
|
@@ -123,17 +193,49 @@ export class Adapter {
|
|
|
123
193
|
}
|
|
124
194
|
else {
|
|
125
195
|
const rustEnum = this.getEnum(sdkEnum);
|
|
126
|
-
this.
|
|
127
|
-
|
|
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);
|
|
128
224
|
}
|
|
129
225
|
const processedTypes = new Set();
|
|
130
226
|
for (const model of this.ctx.sdkPackage.models) {
|
|
131
|
-
if ((model.usage & tcgc.UsageFlags.Input
|
|
227
|
+
if ((model.usage & (tcgc.UsageFlags.Input | tcgc.UsageFlags.Output | tcgc.UsageFlags.Spread | tcgc.UsageFlags.Exception)) === 0) {
|
|
132
228
|
// skip types without input and output usage. this will include core
|
|
133
229
|
// types unless they're explicitly referenced (e.g. a model property).
|
|
134
230
|
// we keep the models for spread params as we internally use them.
|
|
231
|
+
// We also emit exception (error) types.
|
|
135
232
|
continue;
|
|
136
233
|
}
|
|
234
|
+
// Skip the default Azure core error models.
|
|
235
|
+
if (tcgc.isAzureCoreModel(model)) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
needsCoreAndSerde = true;
|
|
137
239
|
// TODO: workaround for https://github.com/Azure/typespec-azure/issues/3614
|
|
138
240
|
if (processedTypes.has(model.name)) {
|
|
139
241
|
continue;
|
|
@@ -144,7 +246,7 @@ export class Adapter {
|
|
|
144
246
|
// END workaround
|
|
145
247
|
if (model.discriminatedSubtypes) {
|
|
146
248
|
const rustUnion = this.getDiscriminatedUnion(model);
|
|
147
|
-
this.
|
|
249
|
+
this.adaptNamespace(model.namespace).unions.push(rustUnion);
|
|
148
250
|
// we don't want to add the base type to the array
|
|
149
251
|
// of models to emit as we hijack its name to be used
|
|
150
252
|
// for the associated tagged enum type.
|
|
@@ -155,9 +257,16 @@ export class Adapter {
|
|
|
155
257
|
}
|
|
156
258
|
else {
|
|
157
259
|
const rustModel = this.getModel(model);
|
|
158
|
-
|
|
260
|
+
if (terminalErrorModelNames.has(model.name)) {
|
|
261
|
+
rustModel.flags |= rust.ModelFlags.Error;
|
|
262
|
+
}
|
|
263
|
+
this.adaptNamespace(model.namespace).models.push(rustModel);
|
|
159
264
|
}
|
|
160
265
|
}
|
|
266
|
+
if (needsCoreAndSerde) {
|
|
267
|
+
this.crate.addDependency(new rust.CrateDependency('azure_core'));
|
|
268
|
+
this.crate.addDependency(new rust.CrateDependency('serde'));
|
|
269
|
+
}
|
|
161
270
|
}
|
|
162
271
|
/**
|
|
163
272
|
* converts a tcgc enum to a Rust enum
|
|
@@ -195,7 +304,7 @@ export class Adapter {
|
|
|
195
304
|
default:
|
|
196
305
|
throw new AdapterError('UnsupportedTsp', `unsupported enum underlying type ${sdkEnum.valueType.kind}`, (_a = sdkEnum.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
197
306
|
}
|
|
198
|
-
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));
|
|
199
308
|
rustEnum.docs = this.adaptDocs(sdkEnum.summary, sdkEnum.doc);
|
|
200
309
|
this.types.set(enumName, rustEnum);
|
|
201
310
|
// the first pass is to detect any enum values that coalesce into duplicate entries
|
|
@@ -306,11 +415,10 @@ export class Adapter {
|
|
|
306
415
|
}
|
|
307
416
|
// include error and LRO polling types as output types
|
|
308
417
|
if ((model.usage & tcgc.UsageFlags.Output) === tcgc.UsageFlags.Output ||
|
|
309
|
-
(model.usage & tcgc.UsageFlags.Exception) === tcgc.UsageFlags.Exception ||
|
|
310
418
|
(model.usage & tcgc.UsageFlags.LroPolling) === tcgc.UsageFlags.LroPolling) {
|
|
311
419
|
modelFlags |= rust.ModelFlags.Output;
|
|
312
420
|
}
|
|
313
|
-
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));
|
|
314
422
|
rustModel.docs = this.adaptDocs(model.summary, model.doc);
|
|
315
423
|
rustModel.xmlName = getXMLName(model.decorators);
|
|
316
424
|
this.types.set(modelName, rustModel);
|
|
@@ -384,7 +492,7 @@ export class Adapter {
|
|
|
384
492
|
* @returns a Rust union
|
|
385
493
|
*/
|
|
386
494
|
getDiscriminatedUnion(src) {
|
|
387
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
495
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
388
496
|
if (src.name.length === 0) {
|
|
389
497
|
throw new AdapterError('InternalError', 'unnamed union', (_a = src.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
390
498
|
}
|
|
@@ -413,20 +521,43 @@ export class Adapter {
|
|
|
413
521
|
if (!discriminatorProperty) {
|
|
414
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);
|
|
415
523
|
}
|
|
416
|
-
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));
|
|
417
525
|
if (discriminatorProperty.type.kind === 'enum' && discriminatorProperty.type.isFixed) {
|
|
418
526
|
// when the DU is a fixed enum, it means we don't fall back to the
|
|
419
527
|
// base type when the discriminator value is unknown or missing.
|
|
420
528
|
rustUnion.unionKind = new rust.DiscriminatedUnionSealed();
|
|
421
529
|
}
|
|
422
530
|
else {
|
|
423
|
-
|
|
531
|
+
const typeName = `Unknown${utils.capitalize(src.name).replace(/\W/g, '')}${utils.capitalize(rustUnion.discriminant)}`;
|
|
532
|
+
const baseModel = this.getModel(src, undefined, typeName);
|
|
533
|
+
const module = this.adaptNamespace(src.namespace);
|
|
534
|
+
if (!module.models.includes(baseModel)) {
|
|
535
|
+
// we only fix up the base model if it's not
|
|
536
|
+
// in the collection of models for this module
|
|
537
|
+
baseModel.flags = rust.ModelFlags.PolymorphicBase;
|
|
538
|
+
// internal serde helper only
|
|
539
|
+
baseModel.visibility = 'pubCrate';
|
|
540
|
+
const lifetime = new rust.Lifetime('a');
|
|
541
|
+
baseModel.lifetime = lifetime;
|
|
542
|
+
for (const field of baseModel.fields) {
|
|
543
|
+
if (field.kind === 'modelField') {
|
|
544
|
+
// strip off the discriminator flag
|
|
545
|
+
field.flags &= ~rust.ModelFieldFlags.Discriminator;
|
|
546
|
+
}
|
|
547
|
+
if (!typeToRefType(field.type)) {
|
|
548
|
+
throw new AdapterError('InternalError', `unsupported field kind ${field.type.kind} in polymorphic base model ${baseModel.name}`, (_d = src.__raw) === null || _d === void 0 ? void 0 : _d.node);
|
|
549
|
+
}
|
|
550
|
+
field.type = this.getRefType(field.type, lifetime);
|
|
551
|
+
}
|
|
552
|
+
module.models.push(baseModel);
|
|
553
|
+
}
|
|
554
|
+
rustUnion.unionKind = new rust.DiscriminatedUnionBase(baseModel);
|
|
424
555
|
}
|
|
425
556
|
for (const subType of Object.values(src.discriminatedSubtypes)) {
|
|
426
557
|
if (!subType.discriminatorValue) {
|
|
427
|
-
throw new AdapterError('InternalError', `model ${subType.name} has no discriminator value`, (
|
|
558
|
+
throw new AdapterError('InternalError', `model ${subType.name} has no discriminator value`, (_e = subType.__raw) === null || _e === void 0 ? void 0 : _e.node);
|
|
428
559
|
}
|
|
429
|
-
const unionMemberType = this.getModel(subType);
|
|
560
|
+
const unionMemberType = subType.discriminatedSubtypes ? this.getDiscriminatedUnion(subType) : this.getModel(subType);
|
|
430
561
|
const rustUnionMember = new rust.DiscriminatedUnionMember(unionMemberType, subType.discriminatorValue);
|
|
431
562
|
rustUnion.members.push(rustUnionMember);
|
|
432
563
|
}
|
|
@@ -437,9 +568,9 @@ export class Adapter {
|
|
|
437
568
|
// we should have verified this earlier.
|
|
438
569
|
// having this check means the compiler won't bark
|
|
439
570
|
// at us when accessing src.discriminatedOptions
|
|
440
|
-
throw new AdapterError('InternalError', 'getDiscriminatedUnion called for non-discriminated union', (
|
|
571
|
+
throw new AdapterError('InternalError', 'getDiscriminatedUnion called for non-discriminated union', (_f = src.__raw) === null || _f === void 0 ? void 0 : _f.node);
|
|
441
572
|
}
|
|
442
|
-
rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), src.discriminatedOptions.discriminatorPropertyName);
|
|
573
|
+
rustUnion = new rust.DiscriminatedUnion(unionName, adaptAccessFlags(src.access), src.discriminatedOptions.discriminatorPropertyName, this.adaptNamespace(src.namespace));
|
|
443
574
|
if (src.discriminatedOptions.envelopePropertyName) {
|
|
444
575
|
rustUnion.unionKind = new rust.DiscriminatedUnionEnvelope(src.discriminatedOptions.envelopePropertyName);
|
|
445
576
|
}
|
|
@@ -460,12 +591,12 @@ export class Adapter {
|
|
|
460
591
|
// END WORKAROUND
|
|
461
592
|
for (const unionMember of src.variantTypes) {
|
|
462
593
|
if (unionMember.kind !== 'model') {
|
|
463
|
-
throw new AdapterError('UnsupportedTsp', `non-model union member kind ${unionMember.kind}`, (
|
|
594
|
+
throw new AdapterError('UnsupportedTsp', `non-model union member kind ${unionMember.kind}`, (_g = unionMember.__raw) === null || _g === void 0 ? void 0 : _g.node);
|
|
464
595
|
}
|
|
465
596
|
// TODO: use unionMember.discriminatorValue
|
|
466
597
|
const discriminatorValue = discriminatorValues.get(unionMember.name);
|
|
467
598
|
if (!discriminatorValue) {
|
|
468
|
-
throw new AdapterError('InternalError', `didn't find discriminant value for type ${unionMember.name}`, (
|
|
599
|
+
throw new AdapterError('InternalError', `didn't find discriminant value for type ${unionMember.name}`, (_h = unionMember.__raw) === null || _h === void 0 ? void 0 : _h.node);
|
|
469
600
|
}
|
|
470
601
|
const unionMemberType = this.getModel(unionMember);
|
|
471
602
|
const rustUnionMember = new rust.DiscriminatedUnionMember(unionMemberType, discriminatorValue);
|
|
@@ -483,6 +614,193 @@ export class Adapter {
|
|
|
483
614
|
var _a, _b, _c, _d, _e;
|
|
484
615
|
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;
|
|
485
616
|
}
|
|
617
|
+
/**
|
|
618
|
+
* converts a non-discriminated tcgc union to either a flat merged Rust Enum (union-of-enums)
|
|
619
|
+
* or a Rust UntaggedUnion (#[serde(untagged)] enum).
|
|
620
|
+
* self-registers the result in the owning module and caches it.
|
|
621
|
+
*
|
|
622
|
+
* @param src the non-discriminated tcgc union
|
|
623
|
+
* @returns a Rust Enum or UntaggedUnion
|
|
624
|
+
*/
|
|
625
|
+
getNonDiscriminatedUnion(src) {
|
|
626
|
+
const unionName = src.name.length > 0
|
|
627
|
+
? utils.deconstruct(src.name).map(each => utils.capitalize(each)).join('')
|
|
628
|
+
: this.synthesizeUnionName(src);
|
|
629
|
+
const keyName = `non-discriminated-union-${unionName}`;
|
|
630
|
+
const cached = this.types.get(keyName);
|
|
631
|
+
if (cached) {
|
|
632
|
+
return cached;
|
|
633
|
+
}
|
|
634
|
+
const result = src.variantTypes.every(v => v.kind === 'enum')
|
|
635
|
+
? this.getMergedFlatEnum(unionName, src)
|
|
636
|
+
: this.buildUntaggedUnion(unionName, src);
|
|
637
|
+
this.types.set(keyName, result);
|
|
638
|
+
const mod = this.adaptNamespace(src.namespace);
|
|
639
|
+
if (result.kind === 'enum') {
|
|
640
|
+
mod.enums.push(result);
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
mod.unions.push(result);
|
|
644
|
+
}
|
|
645
|
+
return result;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* synthesizes a union name for anonymous inline unions by concatenating variant type names
|
|
649
|
+
*
|
|
650
|
+
* @param src the non-discriminated tcgc union
|
|
651
|
+
* @returns a PascalCase name for the union
|
|
652
|
+
*/
|
|
653
|
+
synthesizeUnionName(src) {
|
|
654
|
+
return src.variantTypes
|
|
655
|
+
.map(v => {
|
|
656
|
+
switch (v.kind) {
|
|
657
|
+
case 'model':
|
|
658
|
+
case 'enum':
|
|
659
|
+
return utils.deconstruct(v.name).map(each => utils.capitalize(each)).join('');
|
|
660
|
+
case 'string':
|
|
661
|
+
return 'String';
|
|
662
|
+
case 'boolean':
|
|
663
|
+
return 'Boolean';
|
|
664
|
+
case 'int32':
|
|
665
|
+
return 'Int32';
|
|
666
|
+
case 'int64':
|
|
667
|
+
return 'Int64';
|
|
668
|
+
case 'float32':
|
|
669
|
+
return 'Float32';
|
|
670
|
+
case 'float64':
|
|
671
|
+
return 'Float64';
|
|
672
|
+
case 'array':
|
|
673
|
+
return 'Array';
|
|
674
|
+
default:
|
|
675
|
+
return 'Value';
|
|
676
|
+
}
|
|
677
|
+
})
|
|
678
|
+
.join('');
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* builds a flat merged Rust Enum from a union whose every variant is a TSP enum type.
|
|
682
|
+
* all variant enum values are merged into a single flat enum.
|
|
683
|
+
*
|
|
684
|
+
* @param unionName the PascalCase name for the resulting enum
|
|
685
|
+
* @param src the non-discriminated tcgc union
|
|
686
|
+
* @returns a flat merged Rust Enum
|
|
687
|
+
*/
|
|
688
|
+
getMergedFlatEnum(unionName, src) {
|
|
689
|
+
const rustEnum = new rust.Enum(unionName, adaptAccessFlags(src.access), false, 'String', this.adaptNamespace(src.namespace));
|
|
690
|
+
rustEnum.docs = this.adaptDocs(src.summary, src.doc);
|
|
691
|
+
this.types.set(unionName, rustEnum);
|
|
692
|
+
for (const variant of src.variantTypes) {
|
|
693
|
+
const sdkEnum = variant;
|
|
694
|
+
for (const value of sdkEnum.values) {
|
|
695
|
+
const valueName = naming.fixUpEnumValueName(value);
|
|
696
|
+
if (!rustEnum.values.some(v => v.name === valueName)) {
|
|
697
|
+
const rv = new rust.EnumValue(valueName, rustEnum, value.value);
|
|
698
|
+
rv.docs = this.adaptDocs(value.summary, value.doc);
|
|
699
|
+
rustEnum.values.push(rv);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return rustEnum;
|
|
704
|
+
}
|
|
705
|
+
/**
|
|
706
|
+
* builds an UntaggedUnion (#[serde(untagged)] enum) from a non-discriminated tcgc union
|
|
707
|
+
* whose variants are not all enum types.
|
|
708
|
+
*
|
|
709
|
+
* @param unionName the PascalCase name for the resulting union
|
|
710
|
+
* @param src the non-discriminated tcgc union
|
|
711
|
+
* @returns a Rust UntaggedUnion
|
|
712
|
+
*/
|
|
713
|
+
buildUntaggedUnion(unionName, src) {
|
|
714
|
+
const mod = this.adaptNamespace(src.namespace);
|
|
715
|
+
const rustUnion = new rust.UntaggedUnion(unionName, adaptAccessFlags(src.access), mod);
|
|
716
|
+
rustUnion.docs = this.adaptDocs(src.summary, src.doc);
|
|
717
|
+
// sort variants for correct serde untagged disambiguation:
|
|
718
|
+
// bool(0) < int(1) < float(2) < enum(3) < string(4) < array(5) < model(6)
|
|
719
|
+
const sorted = [...src.variantTypes].sort((a, b) => this.getUntaggedVariantOrder(a) - this.getUntaggedVariantOrder(b));
|
|
720
|
+
for (const variant of sorted) {
|
|
721
|
+
const variantName = this.getUntaggedVariantName(variant);
|
|
722
|
+
const variantType = this.typeToWireType(this.getType(variant));
|
|
723
|
+
const rv = new rust.UntaggedUnionVariant(variantName, variantType);
|
|
724
|
+
rustUnion.variants.push(rv);
|
|
725
|
+
}
|
|
726
|
+
return rustUnion;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* returns the sort order for a variant in a #[serde(untagged)] enum.
|
|
730
|
+
* more specific types must come before more general ones to ensure correct deserialization.
|
|
731
|
+
*
|
|
732
|
+
* @param variant the tcgc type to get the order for
|
|
733
|
+
* @returns a numeric sort key (lower = earlier)
|
|
734
|
+
*/
|
|
735
|
+
getUntaggedVariantOrder(variant) {
|
|
736
|
+
switch (variant.kind) {
|
|
737
|
+
case 'boolean':
|
|
738
|
+
return 0;
|
|
739
|
+
case 'constant':
|
|
740
|
+
return this.getUntaggedVariantOrder(variant.valueType);
|
|
741
|
+
case 'int8':
|
|
742
|
+
case 'int16':
|
|
743
|
+
case 'int32':
|
|
744
|
+
case 'int64':
|
|
745
|
+
case 'safeint':
|
|
746
|
+
return 1;
|
|
747
|
+
case 'float':
|
|
748
|
+
case 'float32':
|
|
749
|
+
case 'float64':
|
|
750
|
+
return 2;
|
|
751
|
+
case 'enum':
|
|
752
|
+
return 3;
|
|
753
|
+
case 'string':
|
|
754
|
+
return 4;
|
|
755
|
+
case 'array':
|
|
756
|
+
return 5;
|
|
757
|
+
case 'model':
|
|
758
|
+
return 6;
|
|
759
|
+
default:
|
|
760
|
+
return 7;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
/**
|
|
764
|
+
* returns the Rust variant name (PascalCase) for a variant in a #[serde(untagged)] enum.
|
|
765
|
+
*
|
|
766
|
+
* @param variant the tcgc type to name
|
|
767
|
+
* @returns a PascalCase Rust variant name
|
|
768
|
+
*/
|
|
769
|
+
getUntaggedVariantName(variant) {
|
|
770
|
+
switch (variant.kind) {
|
|
771
|
+
case 'model':
|
|
772
|
+
return utils.deconstruct(variant.name).map(each => utils.capitalize(each)).join('');
|
|
773
|
+
case 'enum':
|
|
774
|
+
return utils.deconstruct(variant.name).map(each => utils.capitalize(each)).join('');
|
|
775
|
+
case 'boolean':
|
|
776
|
+
return 'Boolean';
|
|
777
|
+
case 'int8':
|
|
778
|
+
return 'Int8';
|
|
779
|
+
case 'int16':
|
|
780
|
+
return 'Int16';
|
|
781
|
+
case 'int32':
|
|
782
|
+
return 'Int32';
|
|
783
|
+
case 'int64':
|
|
784
|
+
return 'Int64';
|
|
785
|
+
case 'safeint':
|
|
786
|
+
return 'SafeInt';
|
|
787
|
+
case 'float':
|
|
788
|
+
case 'float32':
|
|
789
|
+
return 'Float32';
|
|
790
|
+
case 'float64':
|
|
791
|
+
return 'Float64';
|
|
792
|
+
case 'string':
|
|
793
|
+
return 'String';
|
|
794
|
+
case 'constant':
|
|
795
|
+
return this.getUntaggedVariantName(variant.valueType);
|
|
796
|
+
case 'array': {
|
|
797
|
+
const elemName = this.getUntaggedVariantName(variant.valueType);
|
|
798
|
+
return `${elemName}Array`;
|
|
799
|
+
}
|
|
800
|
+
default:
|
|
801
|
+
return 'Value';
|
|
802
|
+
}
|
|
803
|
+
}
|
|
486
804
|
/**
|
|
487
805
|
* converts a tcgc model property to a model field
|
|
488
806
|
*
|
|
@@ -495,7 +813,7 @@ export class Adapter {
|
|
|
495
813
|
getModelField(modelFlags, property, modelVisibility, stack) {
|
|
496
814
|
var _a, _b, _c, _d, _e;
|
|
497
815
|
const fieldNeedsBoxing = function (fieldType) {
|
|
498
|
-
if (fieldType.kind === 'model' && stack.includes(fieldType)) {
|
|
816
|
+
if (fieldType.kind === 'model' && (stack.includes(fieldType))) {
|
|
499
817
|
// if the field's type is a model and it's in the type stack then
|
|
500
818
|
// box it. this is to avoid infinitely recursive type definitions.
|
|
501
819
|
return true;
|
|
@@ -525,6 +843,17 @@ export class Adapter {
|
|
|
525
843
|
const serializedName = (_a = this.getSerializedPropertyName(property)) !== null && _a !== void 0 ? _a : property.name;
|
|
526
844
|
const modelField = new rust.ModelField(naming.getEscapedReservedName(utils.snakeCaseName(property.name), 'prop'), serializedName, modelVisibility, fieldType, property.optional);
|
|
527
845
|
modelField.docs = this.adaptDocs(property.summary, property.doc);
|
|
846
|
+
// append visibility info as a doc comment when visibility is restricted
|
|
847
|
+
const visibilityStr = formatVisibility(property.visibility);
|
|
848
|
+
if (visibilityStr) {
|
|
849
|
+
if (!modelField.docs.description) {
|
|
850
|
+
modelField.docs.description = '';
|
|
851
|
+
}
|
|
852
|
+
else {
|
|
853
|
+
modelField.docs.description += '\n\n';
|
|
854
|
+
}
|
|
855
|
+
modelField.docs.description += `Operational visibility: ${visibilityStr}`;
|
|
856
|
+
}
|
|
528
857
|
// if this is a literal, add a doc comment explaining its behavior
|
|
529
858
|
const unwrappedType = utils.unwrapOption(fieldType);
|
|
530
859
|
if (unwrappedType.kind === 'enumValue' || unwrappedType.kind === 'literal') {
|
|
@@ -600,7 +929,7 @@ export class Adapter {
|
|
|
600
929
|
* @returns the adapted Rust type
|
|
601
930
|
*/
|
|
602
931
|
getType(type, stack) {
|
|
603
|
-
var _a, _b
|
|
932
|
+
var _a, _b;
|
|
604
933
|
if (type.external) {
|
|
605
934
|
return this.getExternalType(type.external);
|
|
606
935
|
}
|
|
@@ -682,6 +1011,9 @@ export class Adapter {
|
|
|
682
1011
|
else if (type.discriminatedSubtypes) {
|
|
683
1012
|
return this.getDiscriminatedUnion(type);
|
|
684
1013
|
}
|
|
1014
|
+
else if (tcgc.isAzureCoreModel(type)) {
|
|
1015
|
+
return this.getExternalType({ kind: 'externalTypeInfo', identity: 'azure_core::error::ErrorDetail' });
|
|
1016
|
+
}
|
|
685
1017
|
return this.getModel(type, stack);
|
|
686
1018
|
case 'endpoint':
|
|
687
1019
|
case 'plainDate':
|
|
@@ -735,12 +1067,12 @@ export class Adapter {
|
|
|
735
1067
|
}
|
|
736
1068
|
case 'union': {
|
|
737
1069
|
if (!type.discriminatedOptions) {
|
|
738
|
-
|
|
1070
|
+
return this.getNonDiscriminatedUnion(type);
|
|
739
1071
|
}
|
|
740
1072
|
return this.getDiscriminatedUnion(type);
|
|
741
1073
|
}
|
|
742
1074
|
default:
|
|
743
|
-
throw new AdapterError('UnsupportedTsp', `unhandled tcgc type ${type.kind}`, (
|
|
1075
|
+
throw new AdapterError('UnsupportedTsp', `unhandled tcgc type ${type.kind}`, (_b = type.__raw) === null || _b === void 0 ? void 0 : _b.node);
|
|
744
1076
|
}
|
|
745
1077
|
}
|
|
746
1078
|
/** returns a Box type */
|
|
@@ -808,11 +1140,12 @@ export class Adapter {
|
|
|
808
1140
|
return optionType;
|
|
809
1141
|
}
|
|
810
1142
|
/** returns the specified type wrapped in a Ref */
|
|
811
|
-
getRefType(type) {
|
|
812
|
-
const typeKey = recursiveKeyName(
|
|
1143
|
+
getRefType(type, lifetime) {
|
|
1144
|
+
const typeKey = recursiveKeyName(`ref${lifetime ? `-${lifetime.name}` : ''}`, type);
|
|
813
1145
|
let refType = this.types.get(typeKey);
|
|
814
1146
|
if (!refType) {
|
|
815
1147
|
refType = new rust.Ref(type);
|
|
1148
|
+
refType.lifetime = lifetime;
|
|
816
1149
|
this.types.set(typeKey, refType);
|
|
817
1150
|
}
|
|
818
1151
|
return refType;
|
|
@@ -961,8 +1294,14 @@ export class Adapter {
|
|
|
961
1294
|
}
|
|
962
1295
|
/** converts all tcgc clients and their methods into Rust clients/methods */
|
|
963
1296
|
adaptClients() {
|
|
1297
|
+
let needsCore = false;
|
|
964
1298
|
for (const client of this.ctx.sdkPackage.clients) {
|
|
1299
|
+
// start with instantiable clients and recursively work down
|
|
965
1300
|
this.recursiveAdaptClient(client);
|
|
1301
|
+
needsCore = true;
|
|
1302
|
+
}
|
|
1303
|
+
if (needsCore) {
|
|
1304
|
+
this.crate.addDependency(new rust.CrateDependency('azure_core'));
|
|
966
1305
|
}
|
|
967
1306
|
}
|
|
968
1307
|
/**
|
|
@@ -989,7 +1328,7 @@ export class Adapter {
|
|
|
989
1328
|
* @returns a Rust client
|
|
990
1329
|
*/
|
|
991
1330
|
recursiveAdaptClient(client, parent) {
|
|
992
|
-
var _a, _b, _c, _d, _e;
|
|
1331
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
993
1332
|
let clientName = client.name;
|
|
994
1333
|
// NOTE: if the client has the @clientName decorator applied then use that verbatim
|
|
995
1334
|
if (parent && !hasClientNameDecorator(client.decorators)) {
|
|
@@ -1012,7 +1351,7 @@ export class Adapter {
|
|
|
1012
1351
|
if (!clientName.match(/Client$/)) {
|
|
1013
1352
|
clientName += 'Client';
|
|
1014
1353
|
}
|
|
1015
|
-
const rustClient = new rust.Client(clientName);
|
|
1354
|
+
const rustClient = new rust.Client(clientName, this.adaptNamespace(client.namespace));
|
|
1016
1355
|
rustClient.docs = this.adaptDocs(client.summary, client.doc);
|
|
1017
1356
|
rustClient.parent = parent;
|
|
1018
1357
|
rustClient.fields.push(new rust.StructField('pipeline', 'pubCrate', new rust.ExternalType(this.crate, 'Pipeline', 'azure_core::http')));
|
|
@@ -1149,10 +1488,16 @@ export class Adapter {
|
|
|
1149
1488
|
break;
|
|
1150
1489
|
}
|
|
1151
1490
|
case 'method': {
|
|
1152
|
-
// https://github.com/Azure/typespec-rust/issues/849
|
|
1153
|
-
// Azure doesn't use optional (with no explicit default value) API versions anyway.
|
|
1154
1491
|
if (param.isApiVersionParam && param.optional && !param.clientDefaultValue) {
|
|
1155
|
-
|
|
1492
|
+
if (!parent && client.children && client.methods.length === 0) {
|
|
1493
|
+
// this is the top-level client for a multi-client scenario.
|
|
1494
|
+
// each sub-client will have its own api-version parameter.
|
|
1495
|
+
// we omit this "merged" api-version param as it's useless.
|
|
1496
|
+
continue;
|
|
1497
|
+
}
|
|
1498
|
+
else {
|
|
1499
|
+
throw new AdapterError('UnsupportedTsp', 'found optional apiVersion parameter with no default value', (_c = param.__raw) === null || _c === void 0 ? void 0 : _c.node);
|
|
1500
|
+
}
|
|
1156
1501
|
}
|
|
1157
1502
|
const clientParam = this.adaptClientParameter(param, rustClient.constructable);
|
|
1158
1503
|
rustClient.fields.push(new rust.StructField(clientParam.name, 'pubCrate', clientParam.type));
|
|
@@ -1190,6 +1535,33 @@ export class Adapter {
|
|
|
1190
1535
|
// as these are handled in the parent client.
|
|
1191
1536
|
continue;
|
|
1192
1537
|
}
|
|
1538
|
+
else if (prop.isApiVersionParam) {
|
|
1539
|
+
// we didn't find the api-version param in the parent which means
|
|
1540
|
+
// this is a multi-client child with its own api-version param.
|
|
1541
|
+
// we need to find the instantiable parent client.
|
|
1542
|
+
let constructable;
|
|
1543
|
+
let cur = parent;
|
|
1544
|
+
while (cur) {
|
|
1545
|
+
constructable = cur.constructable;
|
|
1546
|
+
if (constructable) {
|
|
1547
|
+
break;
|
|
1548
|
+
}
|
|
1549
|
+
cur = cur.parent;
|
|
1550
|
+
}
|
|
1551
|
+
if (!constructable || !cur) {
|
|
1552
|
+
throw new AdapterError('InternalError', `didn't find constructable client for apiVersion in client ${rustClient.name}`, (_d = prop.__raw) === null || _d === void 0 ? void 0 : _d.node);
|
|
1553
|
+
}
|
|
1554
|
+
rustClient.fields.push(new rust.StructField(utils.snakeCaseName(prop.name), 'pubCrate', this.getStringType()));
|
|
1555
|
+
// we use the client name as a prefix to disambiguate the params in the parent
|
|
1556
|
+
const clientParam = this.adaptClientParameter(prop, constructable, utils.deduplicateClientFieldName(rustClient, name));
|
|
1557
|
+
const field = new rust.StructField(clientParam.name, 'pubCrate', clientParam.type);
|
|
1558
|
+
cur.fields.push(field);
|
|
1559
|
+
// propagate this param to all client constructors
|
|
1560
|
+
for (const constructor of constructable.constructors) {
|
|
1561
|
+
constructor.params.push(clientParam);
|
|
1562
|
+
}
|
|
1563
|
+
continue;
|
|
1564
|
+
}
|
|
1193
1565
|
// unique field for this client
|
|
1194
1566
|
rustClient.fields.push(new rust.StructField(name, 'pubCrate', this.getType(prop.type)));
|
|
1195
1567
|
}
|
|
@@ -1197,7 +1569,7 @@ export class Adapter {
|
|
|
1197
1569
|
else {
|
|
1198
1570
|
throw new AdapterError('InternalError', `uninstantiatable client ${client.name} has no parent`);
|
|
1199
1571
|
}
|
|
1200
|
-
for (const child of (
|
|
1572
|
+
for (const child of (_e = client.children) !== null && _e !== void 0 ? _e : []) {
|
|
1201
1573
|
const subClient = this.recursiveAdaptClient(child, rustClient);
|
|
1202
1574
|
this.adaptClientAccessor(client, child, rustClient, subClient);
|
|
1203
1575
|
}
|
|
@@ -1209,7 +1581,7 @@ export class Adapter {
|
|
|
1209
1581
|
code: 'LroPagingNotSupported',
|
|
1210
1582
|
severity: 'warning',
|
|
1211
1583
|
message: `skip emitting Paging LRO ${method.name}`,
|
|
1212
|
-
target: (
|
|
1584
|
+
target: (_g = (_f = method.__raw) === null || _f === void 0 ? void 0 : _f.node) !== null && _g !== void 0 ? _g : tsp.NoTarget,
|
|
1213
1585
|
});
|
|
1214
1586
|
continue;
|
|
1215
1587
|
}
|
|
@@ -1217,7 +1589,7 @@ export class Adapter {
|
|
|
1217
1589
|
}
|
|
1218
1590
|
// Set the tracing namespace for tracing based on the client's namespace
|
|
1219
1591
|
rustClient.languageIndependentName = client.crossLanguageDefinitionId;
|
|
1220
|
-
this.
|
|
1592
|
+
this.adaptNamespace(client.namespace).clients.push(rustClient);
|
|
1221
1593
|
return rustClient;
|
|
1222
1594
|
}
|
|
1223
1595
|
/**
|
|
@@ -1250,11 +1622,12 @@ export class Adapter {
|
|
|
1250
1622
|
*
|
|
1251
1623
|
* @param param the tcgc client parameter to convert
|
|
1252
1624
|
* @param constructable contains client construction info. if the param is optional, it will go in the options type
|
|
1625
|
+
* @param name optional custom name for the parameter
|
|
1253
1626
|
* @returns the Rust client parameter
|
|
1254
1627
|
*/
|
|
1255
|
-
adaptClientParameter(param, constructable) {
|
|
1628
|
+
adaptClientParameter(param, constructable, name) {
|
|
1256
1629
|
let paramType = param.isApiVersionParam ? this.getStringType() : this.getType(param.type);
|
|
1257
|
-
const paramName = utils.snakeCaseName(param.name);
|
|
1630
|
+
const paramName = name !== null && name !== void 0 ? name : utils.snakeCaseName(param.name);
|
|
1258
1631
|
let optional = false;
|
|
1259
1632
|
// client-side default value makes the param optional
|
|
1260
1633
|
if (param.optional || param.clientDefaultValue) {
|
|
@@ -1266,7 +1639,9 @@ export class Adapter {
|
|
|
1266
1639
|
paramField.docs = this.adaptDocs(param.summary, param.doc);
|
|
1267
1640
|
constructable.options.type.fields.push(paramField);
|
|
1268
1641
|
if (param.clientDefaultValue) {
|
|
1269
|
-
|
|
1642
|
+
const constName = `DEFAULT_${paramName.toUpperCase()}`;
|
|
1643
|
+
paramField.defaultValue = `String::from(${constName})`;
|
|
1644
|
+
paramField.defaultValueConstant = { name: constName, value: param.clientDefaultValue };
|
|
1270
1645
|
}
|
|
1271
1646
|
}
|
|
1272
1647
|
let adaptedParam;
|
|
@@ -1319,7 +1694,7 @@ export class Adapter {
|
|
|
1319
1694
|
* @param rustClient the client to which the method belongs
|
|
1320
1695
|
*/
|
|
1321
1696
|
adaptMethod(method, rustClient) {
|
|
1322
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
|
|
1697
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
|
1323
1698
|
let srcMethodName = method.name;
|
|
1324
1699
|
if (method.kind === 'paging' && !srcMethodName.match(/^list/i)) {
|
|
1325
1700
|
const chunks = utils.deconstruct(srcMethodName);
|
|
@@ -1351,7 +1726,7 @@ export class Adapter {
|
|
|
1351
1726
|
const optionsLifetime = new rust.Lifetime('a');
|
|
1352
1727
|
const methodOptionsStruct = new rust.Struct(`${rustClient.name}${utils.pascalCase(srcMethodName, false)}Options`, pub);
|
|
1353
1728
|
methodOptionsStruct.lifetime = optionsLifetime;
|
|
1354
|
-
methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`,
|
|
1729
|
+
methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`, `${utils.buildImportPath(rustClient.module, rustClient.module)}::clients::${rustClient.name}::${methodName}()`)}`;
|
|
1355
1730
|
let clientMethodOptions;
|
|
1356
1731
|
switch (method.kind) {
|
|
1357
1732
|
case 'paging':
|
|
@@ -1367,7 +1742,7 @@ export class Adapter {
|
|
|
1367
1742
|
const methodOptionsField = new rust.StructField('method_options', pub, clientMethodOptions);
|
|
1368
1743
|
methodOptionsField.docs.summary = 'Allows customization of the method call.';
|
|
1369
1744
|
methodOptionsStruct.fields.push(methodOptionsField);
|
|
1370
|
-
const methodOptions = new rust.
|
|
1745
|
+
const methodOptions = new rust.ParameterGroup('options', new rust.Option(methodOptionsStruct));
|
|
1371
1746
|
const httpMethod = method.operation.verb;
|
|
1372
1747
|
let rustMethod;
|
|
1373
1748
|
switch (method.kind) {
|
|
@@ -1397,8 +1772,21 @@ export class Adapter {
|
|
|
1397
1772
|
// https://github.com/microsoft/typespec/blob/f3d792b252c6f40be0e174496d9f34d453676026/packages/http-client-csharp/emitter/src/type/operation-final-state-via.ts#L23-L27
|
|
1398
1773
|
lroFinalResultStrategy = new rust.LroFinalResultStrategyHeader('operation-location');
|
|
1399
1774
|
break;
|
|
1775
|
+
case FinalStateValue.customLink:
|
|
1776
|
+
if (((_d = method.lroMetadata.statusMonitorStep) === null || _d === void 0 ? void 0 : _d.kind) === "nextOperationLink") {
|
|
1777
|
+
const customHeaderName = method.lroMetadata.statusMonitorStep.target.property.name;
|
|
1778
|
+
if (customHeaderName.trim() === '') {
|
|
1779
|
+
throw new AdapterError('UnsupportedTsp', `lroMetadata.finalStateVia === customLink && customHeaderName === "${customHeaderName}"`, (_e = method.__raw) === null || _e === void 0 ? void 0 : _e.node);
|
|
1780
|
+
}
|
|
1781
|
+
lroFinalResultStrategy = new rust.LroFinalResultStrategyHeader(customHeaderName);
|
|
1782
|
+
}
|
|
1783
|
+
else {
|
|
1784
|
+
throw new AdapterError('UnsupportedTsp', `lroMetadata.finalStateVia === customLink && lroMetadata.statusMonitorStep?.kind === "${(_f = method.lroMetadata.statusMonitorStep) === null || _f === void 0 ? void 0 : _f.kind}"`, (_g = method.__raw) === null || _g === void 0 ? void 0 : _g.node);
|
|
1785
|
+
}
|
|
1786
|
+
break;
|
|
1400
1787
|
default:
|
|
1401
|
-
|
|
1788
|
+
/* eslint-disable-next-line @typescript-eslint/restrict-template-expressions */
|
|
1789
|
+
throw new AdapterError('UnsupportedTsp', `lroMetadata.finalStateVia ${method.lroMetadata.finalStateVia} NYI`, (_h = method.__raw) === null || _h === void 0 ? void 0 : _h.node);
|
|
1402
1790
|
}
|
|
1403
1791
|
lroFinalResultStrategy.propertyName = method.lroMetadata.finalResultPath;
|
|
1404
1792
|
}
|
|
@@ -1406,7 +1794,7 @@ export class Adapter {
|
|
|
1406
1794
|
}
|
|
1407
1795
|
break;
|
|
1408
1796
|
default:
|
|
1409
|
-
throw new AdapterError('UnsupportedTsp', `method kind ${method.kind} NYI`, (
|
|
1797
|
+
throw new AdapterError('UnsupportedTsp', `method kind ${method.kind} NYI`, (_j = method.__raw) === null || _j === void 0 ? void 0 : _j.node);
|
|
1410
1798
|
}
|
|
1411
1799
|
rustMethod.docs = this.adaptDocs(method.summary, method.doc);
|
|
1412
1800
|
rustClient.methods.push(rustMethod);
|
|
@@ -1418,44 +1806,21 @@ export class Adapter {
|
|
|
1418
1806
|
}
|
|
1419
1807
|
// maps tcgc method header/query params to their Rust method params
|
|
1420
1808
|
const paramsMap = new Map();
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
//
|
|
1424
|
-
//
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
});
|
|
1430
|
-
});
|
|
1431
|
-
if (!opParam) {
|
|
1432
|
-
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);
|
|
1433
|
-
}
|
|
1434
|
-
if (opParam.kind === 'header' && opParam.serializedName.toLowerCase() === 'x-ms-client-request-id') {
|
|
1435
|
-
// x-ms-client-request-id is automatically inserted into requests via
|
|
1436
|
-
// a pipeline policy. so we don't want to expose this as an actual param.
|
|
1437
|
-
continue;
|
|
1438
|
-
}
|
|
1439
|
-
let adaptedParam;
|
|
1440
|
-
// for spread params there are two cases we need to consider.
|
|
1441
|
-
// if the method param's type doesn't match the op param's type then it's a spread param
|
|
1442
|
-
// - e.g. method param's type is string/int/etc which is a field in the op param's body type
|
|
1443
|
-
// 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
|
|
1444
|
-
// - e.g. op param is an intersection of multiple model types, and each model type is exposed as a discrete param
|
|
1445
|
-
if (opParam.kind === 'body' && opParam.type.kind === 'model'
|
|
1446
|
-
&& (opParam.type.kind !== param.type.kind || opParam.methodParameterSegments.map((segment) => segment[segment.length - 1]).length > 1)) {
|
|
1447
|
-
adaptedParam = this.adaptMethodSpreadParameter(param, this.adaptPayloadFormat(opParam.defaultContentType), opParam.type);
|
|
1448
|
-
}
|
|
1449
|
-
else {
|
|
1450
|
-
adaptedParam = this.adaptMethodParameter(opParam, param);
|
|
1451
|
-
}
|
|
1809
|
+
/** returns true if the op param should be omitted */
|
|
1810
|
+
const skipOpParam = function (opParam) {
|
|
1811
|
+
// x-ms-client-request-id is automatically inserted into requests via
|
|
1812
|
+
// a pipeline policy. so we don't want to expose this as an actual param.
|
|
1813
|
+
return opParam.kind === 'header' && opParam.serializedName.toLowerCase() === 'x-ms-client-request-id';
|
|
1814
|
+
};
|
|
1815
|
+
/** adds adapted params to the method's param list and optional params to method options */
|
|
1816
|
+
const processAdaptedParam = (adaptedParam, methodParam) => {
|
|
1452
1817
|
switch (adaptedParam.kind) {
|
|
1453
1818
|
case 'headerScalar':
|
|
1454
1819
|
case 'queryScalar':
|
|
1455
|
-
paramsMap.set(
|
|
1820
|
+
paramsMap.set(methodParam, adaptedParam);
|
|
1456
1821
|
break;
|
|
1457
1822
|
}
|
|
1458
|
-
adaptedParam.docs = this.adaptDocs(
|
|
1823
|
+
adaptedParam.docs = this.adaptDocs(methodParam.summary, methodParam.doc);
|
|
1459
1824
|
rustMethod.params.push(adaptedParam);
|
|
1460
1825
|
// we specially handle an optional content-type header to ensure it's omitted
|
|
1461
1826
|
// from the options bag type. this shows up when the request body is optional.
|
|
@@ -1467,9 +1832,9 @@ export class Adapter {
|
|
|
1467
1832
|
// sent in the request. we want the field within the model for this param.
|
|
1468
1833
|
// NOTE: if the param is optional then the field is optional, thus it's
|
|
1469
1834
|
// already wrapped in an Option<T> type.
|
|
1470
|
-
const field = adaptedParam.type.content.
|
|
1835
|
+
const field = adaptedParam.type.content.fields.find(f => { return f.name === adaptedParam.name; });
|
|
1471
1836
|
if (!field) {
|
|
1472
|
-
throw new AdapterError('InternalError', `didn't find spread param field ${adaptedParam.name} in type ${adaptedParam.type.content.
|
|
1837
|
+
throw new AdapterError('InternalError', `didn't find spread param field ${adaptedParam.name} in type ${adaptedParam.type.content.name}`);
|
|
1473
1838
|
}
|
|
1474
1839
|
fieldType = field.type;
|
|
1475
1840
|
}
|
|
@@ -1478,7 +1843,124 @@ export class Adapter {
|
|
|
1478
1843
|
}
|
|
1479
1844
|
const optionsField = new rust.StructField(adaptedParam.name, pub, fieldType);
|
|
1480
1845
|
optionsField.docs = adaptedParam.docs;
|
|
1481
|
-
rustMethod.options.type.fields.push(optionsField);
|
|
1846
|
+
rustMethod.options.type.type.fields.push(optionsField);
|
|
1847
|
+
}
|
|
1848
|
+
};
|
|
1849
|
+
for (const param of method.parameters) {
|
|
1850
|
+
// we need to translate from the method param to its underlying operation param.
|
|
1851
|
+
// most params have a one-to-one mapping. however, for spread params, there will
|
|
1852
|
+
// be a many-to-one mapping. i.e. multiple params will map to the same underlying
|
|
1853
|
+
// operation param. each param corresponds to a field within the operation param.
|
|
1854
|
+
// there are three distinct cases
|
|
1855
|
+
// - one-to-one mapping (header param -> header op param)
|
|
1856
|
+
// - many-to-one mapping (spread params, multiple params map to one op param)
|
|
1857
|
+
// - one-to-many mapping (grouped params, one param maps to multiple op params)
|
|
1858
|
+
// two-phase filter: first try strict match on the last segment element (cases 1 and 2).
|
|
1859
|
+
// if no matches, try matching the first segment element (case 3: parameter group from @@override).
|
|
1860
|
+
// this avoids false case-3 matches when a body model has fields extracted as HTTP params
|
|
1861
|
+
// (e.g. @query fields), where segment[0] is the body param name but it's not a parameter group.
|
|
1862
|
+
let opParams = allOpParams.filter((opParam) => {
|
|
1863
|
+
return opParam.methodParameterSegments.some((segment) => segment[segment.length - 1].name === param.name);
|
|
1864
|
+
});
|
|
1865
|
+
if (opParams.length === 0) {
|
|
1866
|
+
opParams = allOpParams.filter((opParam) => {
|
|
1867
|
+
return opParam.methodParameterSegments.some((segment) => segment[0].name === param.name);
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
let adaptedParam;
|
|
1871
|
+
if (opParams.length === 1) {
|
|
1872
|
+
// case 1 or 2
|
|
1873
|
+
const opParam = opParams[0];
|
|
1874
|
+
if (skipOpParam(opParam)) {
|
|
1875
|
+
continue;
|
|
1876
|
+
}
|
|
1877
|
+
// for spread params there are two cases we need to consider.
|
|
1878
|
+
// if the method param's type doesn't match the op param's type then it's a spread param
|
|
1879
|
+
// - e.g. method param's type is string/int/etc which is a field in the op param's body type
|
|
1880
|
+
// 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
|
|
1881
|
+
// - e.g. op param is an intersection of multiple model types, and each model type is exposed as a discrete param
|
|
1882
|
+
if (opParam.kind === 'body' && opParam.type.kind === 'model'
|
|
1883
|
+
&& (opParam.type !== param.type || opParam.methodParameterSegments.map((segment) => segment[segment.length - 1]).length > 1)) {
|
|
1884
|
+
adaptedParam = this.adaptMethodSpreadParameter(param, this.getPayloadFormatType(opParam.type, opParam.defaultContentType), opParam.type);
|
|
1885
|
+
}
|
|
1886
|
+
else {
|
|
1887
|
+
adaptedParam = this.adaptMethodParameter(opParam, param);
|
|
1888
|
+
}
|
|
1889
|
+
processAdaptedParam(adaptedParam, param);
|
|
1890
|
+
}
|
|
1891
|
+
else if (opParams.length > 1) {
|
|
1892
|
+
// case 3 (param group)
|
|
1893
|
+
// Parameter group handling:
|
|
1894
|
+
// - required params stay in the named parameter group
|
|
1895
|
+
// - optional params are moved to the method's options type
|
|
1896
|
+
// - if no required params remain, the param group evaporates
|
|
1897
|
+
let paramGroup;
|
|
1898
|
+
const groupedParams = new Array();
|
|
1899
|
+
const lifetime = new rust.Lifetime('a');
|
|
1900
|
+
let hasRefs = false;
|
|
1901
|
+
for (const opParam of opParams) {
|
|
1902
|
+
if (skipOpParam(opParam)) {
|
|
1903
|
+
continue;
|
|
1904
|
+
}
|
|
1905
|
+
// for grouped params, the segment is [groupMethodParam, fieldProp].
|
|
1906
|
+
// we need the field property to get the correct type for this specific
|
|
1907
|
+
// operation parameter (the opParam's own type might be the group model type).
|
|
1908
|
+
const fieldProp = opParam.methodParameterSegments
|
|
1909
|
+
.flatMap((segment) => segment.length > 1 ? [segment[segment.length - 1]] : [])
|
|
1910
|
+
.find((prop) => prop.name === opParam.name);
|
|
1911
|
+
// pass fieldProp as methodParam so adaptMethodParameter uses the field's type.
|
|
1912
|
+
// cast is safe as we only need .type, .optional, .summary, .doc from it.
|
|
1913
|
+
const groupedParam = this.adaptMethodParameter(opParam, fieldProp, lifetime);
|
|
1914
|
+
const fieldOptional = fieldProp ? fieldProp.optional : opParam.optional;
|
|
1915
|
+
if (!fieldOptional) {
|
|
1916
|
+
if (!paramGroup) {
|
|
1917
|
+
if (param.type.kind !== 'model') {
|
|
1918
|
+
throw new AdapterError('InternalError', `expected model type for parameter group but got ${param.type.kind}`, (_k = param.__raw) === null || _k === void 0 ? void 0 : _k.node);
|
|
1919
|
+
}
|
|
1920
|
+
const groupName = utils.capitalize(param.type.name).replace(/\W/g, '');
|
|
1921
|
+
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(param.name), 'param', reservedParams);
|
|
1922
|
+
// param group's visibility is tied to its matching method
|
|
1923
|
+
const groupStruct = new rust.Struct(groupName, rustMethod.visibility);
|
|
1924
|
+
groupStruct.docs = this.adaptDocs(param.type.summary, param.type.doc);
|
|
1925
|
+
paramGroup = new rust.ParameterGroup(paramName, groupStruct);
|
|
1926
|
+
paramGroup.docs = this.adaptDocs(param.summary, param.doc);
|
|
1927
|
+
}
|
|
1928
|
+
groupedParam.docs = this.adaptDocs(opParam.summary, opParam.doc);
|
|
1929
|
+
groupedParams.push(groupedParam);
|
|
1930
|
+
groupedParam.group = paramGroup;
|
|
1931
|
+
if (groupedParam.type.kind === 'ref') {
|
|
1932
|
+
hasRefs = true;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
else {
|
|
1936
|
+
// optional params within a group are moved to the method's options type
|
|
1937
|
+
processAdaptedParam(groupedParam, param);
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
if (paramGroup) {
|
|
1941
|
+
if (hasRefs) {
|
|
1942
|
+
paramGroup.type.lifetime = lifetime;
|
|
1943
|
+
}
|
|
1944
|
+
// push each grouped param individually; they already have .group set
|
|
1945
|
+
for (const gp of groupedParams) {
|
|
1946
|
+
rustMethod.params.push(gp);
|
|
1947
|
+
}
|
|
1948
|
+
// remove the group's model type from the module's models list.
|
|
1949
|
+
// the parameter group struct will be emitted by the client codegen
|
|
1950
|
+
// in method_options.rs. leaving the model in models.rs would create
|
|
1951
|
+
// a duplicate struct definition with incompatible field types.
|
|
1952
|
+
if (param.type.kind === 'model') {
|
|
1953
|
+
const groupModelName = utils.capitalize(param.type.name).replace(/\W/g, '');
|
|
1954
|
+
const ns = this.adaptNamespace(param.type.namespace);
|
|
1955
|
+
const idx = ns.models.findIndex(m => m.kind === 'model' && m.name === groupModelName);
|
|
1956
|
+
if (idx >= 0) {
|
|
1957
|
+
ns.models.splice(idx, 1);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
else {
|
|
1963
|
+
throw new AdapterError('InternalError', `didn't find operation parameter for method ${method.name} parameter ${param.name}`, (_l = param.__raw) === null || _l === void 0 ? void 0 : _l.node);
|
|
1482
1964
|
}
|
|
1483
1965
|
}
|
|
1484
1966
|
// client params aren't included in method.parameters so
|
|
@@ -1494,6 +1976,7 @@ export class Adapter {
|
|
|
1494
1976
|
var _a;
|
|
1495
1977
|
// fetch the body format from the HTTP responses.
|
|
1496
1978
|
// they should all have the same type so no need to match responses to type.
|
|
1979
|
+
let responseType;
|
|
1497
1980
|
let defaultContentType;
|
|
1498
1981
|
for (const httpResp of method.operation.responses) {
|
|
1499
1982
|
if (!httpResp.defaultContentType) {
|
|
@@ -1505,11 +1988,12 @@ export class Adapter {
|
|
|
1505
1988
|
throw new AdapterError('InternalError', `method ${method.name} has conflicting content types`, (_a = method.__raw) === null || _a === void 0 ? void 0 : _a.node);
|
|
1506
1989
|
}
|
|
1507
1990
|
defaultContentType = httpResp.defaultContentType;
|
|
1991
|
+
responseType = httpResp.type;
|
|
1508
1992
|
}
|
|
1509
1993
|
if (!defaultContentType) {
|
|
1510
1994
|
return 'NoFormat';
|
|
1511
1995
|
}
|
|
1512
|
-
return this.
|
|
1996
|
+
return this.getPayloadFormatType(responseType, defaultContentType);
|
|
1513
1997
|
};
|
|
1514
1998
|
const getStatusCodes = function (httpOp) {
|
|
1515
1999
|
const statusCodes = new Array();
|
|
@@ -1558,25 +2042,26 @@ export class Adapter {
|
|
|
1558
2042
|
}
|
|
1559
2043
|
const responseFormat = getResponseFormat();
|
|
1560
2044
|
if (method.kind === 'paging') {
|
|
1561
|
-
if (responseFormat
|
|
1562
|
-
throw new AdapterError('InternalError', `paged method ${method.name} unexpected response format ${responseFormat}`, (
|
|
2045
|
+
if (responseFormat !== 'JsonFormat' && responseFormat !== 'XmlFormat') {
|
|
2046
|
+
throw new AdapterError('InternalError', `paged method ${method.name} unexpected response format ${responseFormat}`, (_m = method.__raw) === null || _m === void 0 ? void 0 : _m.node);
|
|
1563
2047
|
}
|
|
1564
2048
|
// for paged methods, tcgc models method.response.type as an Array<T>.
|
|
1565
2049
|
// however, we want the synthesized paged response envelope type instead.
|
|
1566
2050
|
const synthesizedType = method.operation.responses[0].type;
|
|
1567
2051
|
if (!synthesizedType) {
|
|
1568
|
-
throw new AdapterError('InternalError', `paged method ${method.name} has no synthesized response type`, (
|
|
2052
|
+
throw new AdapterError('InternalError', `paged method ${method.name} has no synthesized response type`, (_o = method.__raw) === null || _o === void 0 ? void 0 : _o.node);
|
|
1569
2053
|
}
|
|
1570
2054
|
else if (synthesizedType.kind !== 'model') {
|
|
1571
|
-
throw new AdapterError('UnsupportedTsp', `paged method ${method.name} synthesized response type has unexpected kind ${synthesizedType.kind}`, (
|
|
2055
|
+
throw new AdapterError('UnsupportedTsp', `paged method ${method.name} synthesized response type has unexpected kind ${synthesizedType.kind}`, (_p = method.__raw) === null || _p === void 0 ? void 0 : _p.node);
|
|
1572
2056
|
}
|
|
1573
2057
|
const synthesizedModel = this.getModel(synthesizedType, new Array());
|
|
1574
|
-
|
|
1575
|
-
|
|
2058
|
+
const modelNs = this.adaptNamespace(synthesizedType.namespace);
|
|
2059
|
+
if (!modelNs.models.includes(synthesizedModel)) {
|
|
2060
|
+
modelNs.models.push(synthesizedModel);
|
|
1576
2061
|
}
|
|
1577
2062
|
// for the pager response type, remove the Option<T> around the Vec<T> for the page items
|
|
1578
2063
|
if (!method.pagingMetadata.pageItemsSegments) {
|
|
1579
|
-
throw new AdapterError('InternalError', `paged method ${method.name} has no pageItemsSegments`, (
|
|
2064
|
+
throw new AdapterError('InternalError', `paged method ${method.name} has no pageItemsSegments`, (_q = method.__raw) === null || _q === void 0 ? void 0 : _q.node);
|
|
1580
2065
|
}
|
|
1581
2066
|
// unwrap all of the segments for the paged response
|
|
1582
2067
|
let unwrappedCount = 0;
|
|
@@ -1587,13 +2072,13 @@ export class Adapter {
|
|
|
1587
2072
|
switch (responseFormat) {
|
|
1588
2073
|
case 'JsonFormat':
|
|
1589
2074
|
if (segment.kind !== 'property' || !segment.serializationOptions.json) {
|
|
1590
|
-
throw new AdapterError('InternalError', `paged method ${method.name} is missing JSON serialization data`, (
|
|
2075
|
+
throw new AdapterError('InternalError', `paged method ${method.name} is missing JSON serialization data`, (_r = method.__raw) === null || _r === void 0 ? void 0 : _r.node);
|
|
1591
2076
|
}
|
|
1592
2077
|
serde = segment.serializationOptions.json.name;
|
|
1593
2078
|
break;
|
|
1594
2079
|
case 'XmlFormat':
|
|
1595
2080
|
if (segment.kind !== 'property' || !segment.serializationOptions.xml) {
|
|
1596
|
-
throw new AdapterError('InternalError', `paged method ${method.name} is missing XML serialization data`, (
|
|
2081
|
+
throw new AdapterError('InternalError', `paged method ${method.name} is missing XML serialization data`, (_s = method.__raw) === null || _s === void 0 ? void 0 : _s.node);
|
|
1597
2082
|
}
|
|
1598
2083
|
serde = segment.serializationOptions.xml.name;
|
|
1599
2084
|
break;
|
|
@@ -1619,45 +2104,55 @@ export class Adapter {
|
|
|
1619
2104
|
}
|
|
1620
2105
|
}
|
|
1621
2106
|
if (unwrappedCount !== method.pagingMetadata.pageItemsSegments.length) {
|
|
1622
|
-
throw new AdapterError('InternalError', `failed to unwrap paged items for method ${method.name}`, (
|
|
2107
|
+
throw new AdapterError('InternalError', `failed to unwrap paged items for method ${method.name}`, (_t = method.__raw) === null || _t === void 0 ? void 0 : _t.node);
|
|
1623
2108
|
}
|
|
1624
2109
|
this.crate.addDependency(new rust.CrateDependency('async-trait'));
|
|
1625
2110
|
// default to nextLink. will update it as required when we have that info
|
|
1626
2111
|
rustMethod.returns = new rust.Result(this.crate, new rust.Pager(this.crate, new rust.Response(this.crate, synthesizedModel, responseFormat), 'nextLink'));
|
|
1627
2112
|
}
|
|
1628
2113
|
else if (method.kind === 'lro') {
|
|
1629
|
-
const
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
2114
|
+
const pushModels = (tcgcType, container, rustType = this.getType(tcgcType), ignoreAzureCoreModels = true) => {
|
|
2115
|
+
switch (tcgcType.kind) {
|
|
2116
|
+
case 'array':
|
|
2117
|
+
pushModels(tcgcType.valueType, container);
|
|
2118
|
+
return;
|
|
2119
|
+
case 'model':
|
|
2120
|
+
if (ignoreAzureCoreModels && tcgc.isAzureCoreModel(tcgcType)) {
|
|
2121
|
+
return;
|
|
2122
|
+
}
|
|
2123
|
+
break;
|
|
2124
|
+
default:
|
|
2125
|
+
return;
|
|
2126
|
+
}
|
|
2127
|
+
if (rustType.kind !== 'model' || container.models.some(m => m === rustType) || rustType.module !== rustClient.module) {
|
|
2128
|
+
// model already exists or belongs to a different module, do nothing
|
|
1636
2129
|
return;
|
|
1637
2130
|
}
|
|
1638
|
-
|
|
1639
|
-
for (const
|
|
1640
|
-
|
|
1641
|
-
fieldType = fieldType.kind === 'option' ? fieldType.type : fieldType;
|
|
1642
|
-
fieldType = fieldType.kind === 'Vec' ? fieldType.type : fieldType;
|
|
1643
|
-
if (fieldType.kind === 'model') {
|
|
1644
|
-
pushModels(fieldType, crate);
|
|
1645
|
-
}
|
|
2131
|
+
container.models.push(rustType);
|
|
2132
|
+
for (const tcgcField of tcgcType.properties) {
|
|
2133
|
+
pushModels(tcgcField.type, container);
|
|
1646
2134
|
}
|
|
1647
2135
|
};
|
|
1648
|
-
|
|
2136
|
+
const format = responseFormat === 'JsonFormat' || responseFormat === 'XmlFormat' ? responseFormat : 'JsonFormat';
|
|
2137
|
+
const statusModel = this.getModel(method.lroMetadata.pollingInfo.responseModel, undefined, `${rustClient.name}${utils.pascalCase(rustMethod.name, false)}OperationStatus`);
|
|
2138
|
+
const statusType = this.typeToWireType(statusModel);
|
|
2139
|
+
if (statusType.kind !== 'model') {
|
|
2140
|
+
throw new AdapterError('InternalError', `status type for an LRO method '${method.name}' is not a model`, (_u = method.__raw) === null || _u === void 0 ? void 0 : _u.node);
|
|
2141
|
+
}
|
|
2142
|
+
// the adapted type likely comes from core which means
|
|
2143
|
+
// it will be in the root namespace. since we emit these
|
|
2144
|
+
// per client.operation, set the correct module
|
|
2145
|
+
statusType.module = rustClient.module;
|
|
2146
|
+
pushModels(method.lroMetadata.pollingInfo.responseModel, rustClient.module, statusModel, false);
|
|
1649
2147
|
const poller = new rust.Poller(this.crate, new rust.Response(this.crate, statusType, format));
|
|
1650
|
-
if (
|
|
1651
|
-
const
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
}
|
|
1655
|
-
pushModels(responseType, this.crate);
|
|
1656
|
-
poller.resultType = new rust.Response(this.crate, responseType, format);
|
|
2148
|
+
if (method.response.type) {
|
|
2149
|
+
const resultType = this.getType(method.response.type);
|
|
2150
|
+
pushModels(method.response.type, rustClient.module, resultType);
|
|
2151
|
+
poller.resultType = new rust.Response(this.crate, this.typeToWireType(resultType), format);
|
|
1657
2152
|
}
|
|
1658
2153
|
rustMethod.returns = new rust.Result(this.crate, poller);
|
|
1659
2154
|
}
|
|
1660
|
-
else if (method.response.type &&
|
|
2155
|
+
else if (method.response.type && responseFormat !== 'BinaryFormat') {
|
|
1661
2156
|
const response = new rust.Response(this.crate, this.typeToWireType(this.getType(method.response.type)), responseFormat);
|
|
1662
2157
|
rustMethod.returns = new rust.Result(this.crate, response);
|
|
1663
2158
|
}
|
|
@@ -1666,9 +2161,9 @@ export class Adapter {
|
|
|
1666
2161
|
// we need to return a marker type
|
|
1667
2162
|
const markerType = new rust.MarkerType(`${rustClient.name}${utils.pascalCase(method.name, false)}Result`, rustMethod.visibility);
|
|
1668
2163
|
markerType.docs.summary = `Contains results for ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
|
|
1669
|
-
|
|
2164
|
+
rustClient.module.models.push(markerType);
|
|
1670
2165
|
let resultType;
|
|
1671
|
-
if (
|
|
2166
|
+
if (responseFormat === 'BinaryFormat') {
|
|
1672
2167
|
// method returns a streaming binary response with headers
|
|
1673
2168
|
resultType = new rust.AsyncResponse(this.crate, markerType);
|
|
1674
2169
|
}
|
|
@@ -1677,8 +2172,8 @@ export class Adapter {
|
|
|
1677
2172
|
}
|
|
1678
2173
|
rustMethod.returns = new rust.Result(this.crate, resultType);
|
|
1679
2174
|
}
|
|
1680
|
-
else if (
|
|
1681
|
-
//
|
|
2175
|
+
else if (responseFormat === 'BinaryFormat') {
|
|
2176
|
+
// binary format indicates a streaming binary response
|
|
1682
2177
|
rustMethod.returns = new rust.Result(this.crate, new rust.AsyncResponse(this.crate, this.getUnitType()));
|
|
1683
2178
|
}
|
|
1684
2179
|
else {
|
|
@@ -1699,13 +2194,13 @@ export class Adapter {
|
|
|
1699
2194
|
// can't do this until the method has been completely adapted
|
|
1700
2195
|
const pageableMethod = rustMethod;
|
|
1701
2196
|
pageableMethod.strategy = this.adaptPageableMethodStrategy(method, paramsMap, responseHeadersMap);
|
|
1702
|
-
if (((
|
|
2197
|
+
if (((_v = pageableMethod.strategy) === null || _v === void 0 ? void 0 : _v.kind) === 'nextLink') {
|
|
1703
2198
|
pageableMethod.strategy.reinjectedParams = this.adaptPageableMethodReinjectionParams(method, paramsMap);
|
|
1704
2199
|
}
|
|
1705
|
-
else if (((
|
|
2200
|
+
else if (((_w = pageableMethod.strategy) === null || _w === void 0 ? void 0 : _w.kind) === 'continuationToken') {
|
|
1706
2201
|
// set the continuation type to token on the Pager and the PagerOptions field in the method options
|
|
1707
2202
|
pageableMethod.returns.type.continuation = 'token';
|
|
1708
|
-
for (const field of pageableMethod.options.type.fields) {
|
|
2203
|
+
for (const field of pageableMethod.options.type.type.fields) {
|
|
1709
2204
|
if (field.type.kind === 'pagerOptions') {
|
|
1710
2205
|
field.type.continuation = 'token';
|
|
1711
2206
|
}
|
|
@@ -1770,6 +2265,8 @@ export class Adapter {
|
|
|
1770
2265
|
return type.name;
|
|
1771
2266
|
case 'hashmap':
|
|
1772
2267
|
return `${type.name}${recursiveTypeName(type.type)}`;
|
|
2268
|
+
case 'option':
|
|
2269
|
+
return `Option${recursiveTypeName(type.type)}`;
|
|
1773
2270
|
case 'ref':
|
|
1774
2271
|
return `Ref${recursiveTypeName(type.type)}`;
|
|
1775
2272
|
case 'scalar':
|
|
@@ -1812,7 +2309,7 @@ export class Adapter {
|
|
|
1812
2309
|
const traitName = `${recursiveTypeName(implFor.kind === 'asyncResponse' ? implFor.type : implFor.content)}Headers`;
|
|
1813
2310
|
// NOTE: the complete doc text will be emitted at codegen time
|
|
1814
2311
|
const docs = this.asDocLink(`${client.name}::${method.name}()`, `crate::generated::clients::${client.name}::${method.name}()`);
|
|
1815
|
-
const responseHeadersTrait = new rust.ResponseHeadersTrait(traitName, implFor, docs, method.visibility);
|
|
2312
|
+
const responseHeadersTrait = new rust.ResponseHeadersTrait(traitName, implFor, docs, method.visibility, client.module);
|
|
1816
2313
|
responseHeadersTrait.headers.push(...responseHeaders);
|
|
1817
2314
|
return responseHeadersTrait;
|
|
1818
2315
|
}
|
|
@@ -1940,9 +2437,10 @@ export class Adapter {
|
|
|
1940
2437
|
*
|
|
1941
2438
|
* @param opParam the tcgc operation parameter to convert
|
|
1942
2439
|
* @param methodParam the tcgc method parameter associated with opParam
|
|
2440
|
+
* @param lifetime optional lifetime to stamp on borrowed ref types (used for grouped params that are struct fields)
|
|
1943
2441
|
* @returns a Rust method parameter
|
|
1944
2442
|
*/
|
|
1945
|
-
adaptMethodParameter(opParam, methodParam) {
|
|
2443
|
+
adaptMethodParameter(opParam, methodParam, lifetime) {
|
|
1946
2444
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1947
2445
|
/**
|
|
1948
2446
|
* used to create keys for this.clientMethodParams
|
|
@@ -1976,9 +2474,9 @@ export class Adapter {
|
|
|
1976
2474
|
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(getCorrespondingClientParamName(opParam)), 'param', reservedParams);
|
|
1977
2475
|
const paramOptional = methodParam ? methodParam.optional : opParam.optional;
|
|
1978
2476
|
let paramType = this.getType(methodParam ? methodParam.type : opParam.type);
|
|
1979
|
-
// for required header/path/query method string params, we might emit them as borrowed types
|
|
2477
|
+
// for required header/path/query method string params, we might emit them as borrowed types.
|
|
1980
2478
|
if (!paramOptional && paramLoc !== 'client' && (opParam.kind === 'header' || opParam.kind === 'path' || opParam.kind === 'query')) {
|
|
1981
|
-
const borrowedType = this.canBorrowMethodParam(paramType, opParam.kind);
|
|
2479
|
+
const borrowedType = this.canBorrowMethodParam(paramType, opParam.kind, lifetime);
|
|
1982
2480
|
if (borrowedType) {
|
|
1983
2481
|
paramType = borrowedType;
|
|
1984
2482
|
}
|
|
@@ -1987,15 +2485,17 @@ export class Adapter {
|
|
|
1987
2485
|
switch (opParam.kind) {
|
|
1988
2486
|
case 'body': {
|
|
1989
2487
|
let requestType;
|
|
1990
|
-
|
|
1991
|
-
|
|
2488
|
+
const requestFormatType = this.getPayloadFormatType(opParam.type, opParam.defaultContentType);
|
|
2489
|
+
if (requestFormatType === 'BinaryFormat') {
|
|
2490
|
+
// binary format indicates a streaming binary request
|
|
1992
2491
|
requestType = new rust.Bytes(this.crate);
|
|
1993
2492
|
}
|
|
1994
2493
|
else {
|
|
1995
|
-
requestType =
|
|
2494
|
+
requestType = this.typeToWireType(paramType);
|
|
1996
2495
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
2496
|
+
// binary payloads use NoFormat as the body is sent as raw bytes
|
|
2497
|
+
const wireFormat = requestFormatType === 'BinaryFormat' ? 'NoFormat' : requestFormatType;
|
|
2498
|
+
adaptedParam = new rust.BodyParameter(paramName, paramLoc, paramOptional, new rust.RequestContent(this.crate, requestType, wireFormat));
|
|
1999
2499
|
break;
|
|
2000
2500
|
}
|
|
2001
2501
|
case 'cookie':
|
|
@@ -2115,17 +2615,18 @@ export class Adapter {
|
|
|
2115
2615
|
*
|
|
2116
2616
|
* @param type the param type to be updated
|
|
2117
2617
|
* @param kind the kind of param
|
|
2618
|
+
* @param lifetime optional lifetime to stamp on Ref types (used for struct fields)
|
|
2118
2619
|
* @returns the updated param type or undefined
|
|
2119
2620
|
*/
|
|
2120
|
-
canBorrowMethodParam(type, kind) {
|
|
2621
|
+
canBorrowMethodParam(type, kind, lifetime) {
|
|
2121
2622
|
const recursiveBuildVecStr = (v) => {
|
|
2122
2623
|
switch (v.kind) {
|
|
2123
2624
|
case 'encodedBytes':
|
|
2124
|
-
return this.getRefType(this.getEncodedBytes(v.encoding, true));
|
|
2625
|
+
return this.getRefType(this.getEncodedBytes(v.encoding, true), lifetime);
|
|
2125
2626
|
case 'hashmap':
|
|
2126
2627
|
return this.getHashMap(this.typeToWireType(recursiveBuildVecStr(v.type)));
|
|
2127
2628
|
case 'String':
|
|
2128
|
-
return this.getRefType(this.getStringSlice());
|
|
2629
|
+
return this.getRefType(this.getStringSlice(), lifetime);
|
|
2129
2630
|
case 'Vec':
|
|
2130
2631
|
return this.getVec(this.typeToWireType(recursiveBuildVecStr(v.type)));
|
|
2131
2632
|
default:
|
|
@@ -2142,19 +2643,19 @@ export class Adapter {
|
|
|
2142
2643
|
case 'String':
|
|
2143
2644
|
// header String params are always owned
|
|
2144
2645
|
if (kind !== 'header') {
|
|
2145
|
-
return this.getRefType(this.getStringSlice());
|
|
2646
|
+
return this.getRefType(this.getStringSlice(), lifetime);
|
|
2146
2647
|
}
|
|
2147
2648
|
break;
|
|
2148
2649
|
case 'Vec': {
|
|
2149
2650
|
// if this is an array of string, we ultimately want a slice of &str
|
|
2150
2651
|
const unwrapped = recursiveUnwrapVec(type);
|
|
2151
2652
|
if (unwrapped.kind === 'String' || unwrapped.kind === 'encodedBytes') {
|
|
2152
|
-
return this.getRefType(this.getSlice(recursiveBuildVecStr(type.type)));
|
|
2653
|
+
return this.getRefType(this.getSlice(recursiveBuildVecStr(type.type)), lifetime);
|
|
2153
2654
|
}
|
|
2154
|
-
return this.getRefType(this.getSlice(type.type));
|
|
2655
|
+
return this.getRefType(this.getSlice(type.type), lifetime);
|
|
2155
2656
|
}
|
|
2156
2657
|
case 'encodedBytes':
|
|
2157
|
-
return this.getRefType(this.getEncodedBytes(type.encoding, true));
|
|
2658
|
+
return this.getRefType(this.getEncodedBytes(type.encoding, true), lifetime);
|
|
2158
2659
|
case 'decimal':
|
|
2159
2660
|
case 'Etag':
|
|
2160
2661
|
case 'hashmap':
|
|
@@ -2164,7 +2665,7 @@ export class Adapter {
|
|
|
2164
2665
|
case 'Url':
|
|
2165
2666
|
// these types all require conversion
|
|
2166
2667
|
// to String so we don't need to own them
|
|
2167
|
-
return this.getRefType(type);
|
|
2668
|
+
return this.getRefType(type, lifetime);
|
|
2168
2669
|
}
|
|
2169
2670
|
return undefined;
|
|
2170
2671
|
}
|
|
@@ -2196,6 +2697,7 @@ export class Adapter {
|
|
|
2196
2697
|
case 'slice':
|
|
2197
2698
|
case 'str':
|
|
2198
2699
|
case 'String':
|
|
2700
|
+
case 'untaggedUnion':
|
|
2199
2701
|
case 'Url':
|
|
2200
2702
|
case 'Vec':
|
|
2201
2703
|
return type;
|
|
@@ -2231,46 +2733,41 @@ export class Adapter {
|
|
|
2231
2733
|
}
|
|
2232
2734
|
const paramName = naming.getEscapedReservedName(utils.snakeCaseName(param.name), 'param');
|
|
2233
2735
|
const paramLoc = 'method';
|
|
2234
|
-
const
|
|
2235
|
-
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));
|
|
2736
|
+
const adaptedParam = new rust.PartialBodyParameter(paramName, paramLoc, param.optional, serializedName, this.getType(param.type), new rust.RequestContent(this.crate, payloadType, format));
|
|
2236
2737
|
return adaptedParam;
|
|
2237
2738
|
}
|
|
2238
2739
|
/**
|
|
2239
|
-
*
|
|
2740
|
+
* determines the payload format type from serializationOptions on the type, falling
|
|
2741
|
+
* back to inspecting the defaultContentType when serializationOptions isn't available.
|
|
2240
2742
|
*
|
|
2241
|
-
* @param
|
|
2242
|
-
* @
|
|
2743
|
+
* @param type the SDK type associated with the payload, if available
|
|
2744
|
+
* @param defaultContentType the value of the Accept or Content-Type header
|
|
2745
|
+
* @returns a payload format type
|
|
2243
2746
|
*/
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2747
|
+
getPayloadFormatType(type, defaultContentType) {
|
|
2748
|
+
if ((type === null || type === void 0 ? void 0 : type.kind) === 'model') {
|
|
2749
|
+
const opts = type.serializationOptions;
|
|
2750
|
+
if (opts.json) {
|
|
2751
|
+
return 'JsonFormat';
|
|
2752
|
+
}
|
|
2753
|
+
else if (opts.xml) {
|
|
2754
|
+
this.crate.addDependency(new rust.CrateDependency('azure_core', ['xml']));
|
|
2755
|
+
return 'XmlFormat';
|
|
2756
|
+
}
|
|
2757
|
+
else if (opts.binary) {
|
|
2758
|
+
return 'BinaryFormat';
|
|
2759
|
+
}
|
|
2256
2760
|
}
|
|
2257
|
-
else {
|
|
2258
|
-
|
|
2761
|
+
else if ((type === null || type === void 0 ? void 0 : type.kind) === 'bytes' && type.encode === 'bytes') {
|
|
2762
|
+
// fallback: check if it's a bytes type with bytes encoding (binary stream)
|
|
2763
|
+
return 'BinaryFormat';
|
|
2259
2764
|
}
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
*
|
|
2264
|
-
* @param contentType the value of the Accept or Content-Type header
|
|
2265
|
-
* @returns a response format
|
|
2266
|
-
*/
|
|
2267
|
-
adaptPayloadFormatType(contentType) {
|
|
2268
|
-
// we only recognize/support JSON and XML content types.
|
|
2269
|
-
// anything else is NoFormat
|
|
2270
|
-
if (contentType.match(/json/i)) {
|
|
2765
|
+
// tcgc doesn't yet have serializationOptions on types other
|
|
2766
|
+
// than models. for those cases, fall back to the header value
|
|
2767
|
+
if (defaultContentType.match(/json/i)) {
|
|
2271
2768
|
return 'JsonFormat';
|
|
2272
2769
|
}
|
|
2273
|
-
else if (
|
|
2770
|
+
else if (defaultContentType.match(/xml/i)) {
|
|
2274
2771
|
// XML support is disabled by default
|
|
2275
2772
|
this.crate.addDependency(new rust.CrateDependency('azure_core', ['xml']));
|
|
2276
2773
|
return 'XmlFormat';
|
|
@@ -2305,6 +2802,7 @@ function recursiveKeyName(root, type) {
|
|
|
2305
2802
|
switch (type.kind) {
|
|
2306
2803
|
case 'Vec':
|
|
2307
2804
|
case 'box':
|
|
2805
|
+
case 'option':
|
|
2308
2806
|
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
2309
2807
|
case 'encodedBytes':
|
|
2310
2808
|
return `${root}-${type.kind}-${type.encoding}${type.slice ? '-slice' : ''}`;
|
|
@@ -2319,15 +2817,14 @@ function recursiveKeyName(root, type) {
|
|
|
2319
2817
|
case 'discriminatedUnion':
|
|
2320
2818
|
case 'model':
|
|
2321
2819
|
case 'struct':
|
|
2820
|
+
case 'untaggedUnion':
|
|
2322
2821
|
return `${root}-${type.kind}-${type.name}`;
|
|
2323
2822
|
case 'literal':
|
|
2324
2823
|
return `${recursiveKeyName(`${root}-${type.kind}`, type.valueKind)}-${type.value}`;
|
|
2325
|
-
case 'payload':
|
|
2326
|
-
return recursiveKeyName(`${root}-${type.kind}-${type.format}`, type.type);
|
|
2327
2824
|
case 'ref':
|
|
2328
|
-
return recursiveKeyName(`${root}-${type.kind}`, type.type);
|
|
2825
|
+
return recursiveKeyName(`${root}-${type.kind}${type.lifetime ? `-${type.lifetime.name}` : ''}`, type.type);
|
|
2329
2826
|
case 'requestContent':
|
|
2330
|
-
return recursiveKeyName(`${root}-${type.kind}`, type.content);
|
|
2827
|
+
return recursiveKeyName(`${root}-${type.kind}-${type.format}`, type.content);
|
|
2331
2828
|
case 'safeint':
|
|
2332
2829
|
case 'decimal':
|
|
2333
2830
|
return `${root}-${type.kind}${type.stringEncoding ? '-string' : ''}`;
|
|
@@ -2417,9 +2914,99 @@ function isHttpStatusCodeRange(statusCode) {
|
|
|
2417
2914
|
function adaptAccessFlags(access) {
|
|
2418
2915
|
return access === 'public' ? 'pub' : 'pubCrate';
|
|
2419
2916
|
}
|
|
2917
|
+
/**
|
|
2918
|
+
* converts an array of Visibility flags to a sorted, human-readable string.
|
|
2919
|
+
* returns undefined if visibility is unrestricted (all flags or undefined).
|
|
2920
|
+
*
|
|
2921
|
+
* @param visibility the visibility flags from TCGC
|
|
2922
|
+
* @returns a formatted string like "Read" or "Create, Update", or undefined if unrestricted
|
|
2923
|
+
*/
|
|
2924
|
+
export function formatVisibility(visibility) {
|
|
2925
|
+
if (!visibility || visibility.length === 0) {
|
|
2926
|
+
return undefined;
|
|
2927
|
+
}
|
|
2928
|
+
// combine all flags into a single bitmask
|
|
2929
|
+
let combined = 0;
|
|
2930
|
+
for (const v of visibility) {
|
|
2931
|
+
combined |= v;
|
|
2932
|
+
}
|
|
2933
|
+
// if all lifecycle flags are set, there's no restriction
|
|
2934
|
+
if ((combined & http.Visibility.All) === http.Visibility.All) {
|
|
2935
|
+
return undefined;
|
|
2936
|
+
}
|
|
2937
|
+
// flags are checked in sorted order: Create, Delete, Query, Read, Update
|
|
2938
|
+
const names = [];
|
|
2939
|
+
if (combined & http.Visibility.Create) {
|
|
2940
|
+
names.push('Create');
|
|
2941
|
+
}
|
|
2942
|
+
if (combined & http.Visibility.Delete) {
|
|
2943
|
+
names.push('Delete');
|
|
2944
|
+
}
|
|
2945
|
+
if (combined & http.Visibility.Query) {
|
|
2946
|
+
names.push('Query');
|
|
2947
|
+
}
|
|
2948
|
+
if (combined & http.Visibility.Read) {
|
|
2949
|
+
names.push('Read');
|
|
2950
|
+
}
|
|
2951
|
+
if (combined & http.Visibility.Update) {
|
|
2952
|
+
names.push('Update');
|
|
2953
|
+
}
|
|
2954
|
+
return names.length > 0 ? names.join(', ') : undefined;
|
|
2955
|
+
}
|
|
2420
2956
|
/** contains reserved param names */
|
|
2421
2957
|
const reservedParams = new Set([
|
|
2422
2958
|
// reserved per SDK guidelines
|
|
2423
2959
|
'options',
|
|
2424
2960
|
]);
|
|
2961
|
+
/**
|
|
2962
|
+
* possible namespaces from core libraries.
|
|
2963
|
+
* all types from these namespaces will be placed
|
|
2964
|
+
* into the root namespace.
|
|
2965
|
+
* if more core libs show up, add their namespaces here.
|
|
2966
|
+
*/
|
|
2967
|
+
const LIB_NAMESPACE = [
|
|
2968
|
+
"azure.clientgenerator.core",
|
|
2969
|
+
"azure.core",
|
|
2970
|
+
"azure.resourcemanager",
|
|
2971
|
+
"typespec.http",
|
|
2972
|
+
"typespec.rest",
|
|
2973
|
+
"typespec.versioning",
|
|
2974
|
+
];
|
|
2975
|
+
/** returns true if the given namespace is a known library namespace or a child of one */
|
|
2976
|
+
function isLibNamespace(namespace) {
|
|
2977
|
+
const ns = namespace.toLowerCase();
|
|
2978
|
+
return LIB_NAMESPACE.some((lib) => ns === lib || ns.startsWith(lib + '.'));
|
|
2979
|
+
}
|
|
2980
|
+
/**
|
|
2981
|
+
* narrows type to a RefType within the conditional block.
|
|
2982
|
+
* this function throws if the narrowing is not possible.
|
|
2983
|
+
*/
|
|
2984
|
+
function typeToRefType(type) {
|
|
2985
|
+
switch (type.kind) {
|
|
2986
|
+
case 'bytes':
|
|
2987
|
+
case 'decimal':
|
|
2988
|
+
case 'discriminatedUnion':
|
|
2989
|
+
case 'encodedBytes':
|
|
2990
|
+
case 'enum':
|
|
2991
|
+
case 'enumValue':
|
|
2992
|
+
case 'Etag':
|
|
2993
|
+
case 'external':
|
|
2994
|
+
case 'hashmap':
|
|
2995
|
+
case 'jsonValue':
|
|
2996
|
+
case 'model':
|
|
2997
|
+
case 'offsetDateTime':
|
|
2998
|
+
case 'option':
|
|
2999
|
+
case 'safeint':
|
|
3000
|
+
case 'scalar':
|
|
3001
|
+
case 'slice':
|
|
3002
|
+
case 'str':
|
|
3003
|
+
case 'String':
|
|
3004
|
+
case 'untaggedUnion':
|
|
3005
|
+
case 'Url':
|
|
3006
|
+
case 'Vec':
|
|
3007
|
+
return true;
|
|
3008
|
+
default:
|
|
3009
|
+
throw new AdapterError('InternalError', `cannot convert ${type.kind} to a ref type`);
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
2425
3012
|
//# sourceMappingURL=adapter.js.map
|