@azure-tools/typespec-rust 0.13.0 → 0.13.2
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.map +1 -1
- package/dist/src/codegen/clients.js +14 -14
- package/dist/src/codegen/clients.js.map +1 -1
- package/dist/src/codegen/context.js +6 -6
- package/dist/src/codegen/context.js.map +1 -1
- package/dist/src/codegen/enums.js +2 -2
- package/dist/src/codegen/enums.js.map +1 -1
- package/dist/src/codegen/headerTraits.d.ts.map +1 -1
- package/dist/src/codegen/headerTraits.js +48 -56
- package/dist/src/codegen/headerTraits.js.map +1 -1
- package/dist/src/codegen/helpers.d.ts +4 -3
- package/dist/src/codegen/helpers.d.ts.map +1 -1
- package/dist/src/codegen/helpers.js +14 -7
- package/dist/src/codegen/helpers.js.map +1 -1
- package/dist/src/codegen/lib.js +1 -1
- package/dist/src/codegen/lib.js.map +1 -1
- package/dist/src/codegen/models.js +15 -14
- package/dist/src/codegen/models.js.map +1 -1
- package/dist/src/codegen/use.d.ts +4 -11
- package/dist/src/codegen/use.d.ts.map +1 -1
- package/dist/src/codegen/use.js +103 -62
- package/dist/src/codegen/use.js.map +1 -1
- package/dist/src/codemodel/client.d.ts +30 -10
- package/dist/src/codemodel/client.d.ts.map +1 -1
- package/dist/src/codemodel/client.js +14 -5
- package/dist/src/codemodel/client.js.map +1 -1
- package/dist/src/codemodel/crate.js +2 -1
- package/dist/src/codemodel/crate.js.map +1 -1
- package/dist/src/codemodel/types.d.ts +54 -44
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +37 -65
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/emitter.d.ts.map +1 -1
- package/dist/src/emitter.js +2 -1
- package/dist/src/emitter.js.map +1 -1
- package/dist/src/lib.d.ts +1 -0
- package/dist/src/lib.d.ts.map +1 -1
- package/dist/src/lib.js +1 -0
- package/dist/src/lib.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +45 -0
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +215 -55
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/package.json +24 -24
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/tcgcadapter/adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAyB,WAAW,EAAY,MAAM,oBAAoB,CAAC;AAIlF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C,2DAA2D;AAC3D,qBAAa,OAAO;IAClB;;;;;OAKG;WACU,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAY/E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/tcgcadapter/adapter.ts"],"names":[],"mappings":"AAOA,OAAO,EAAyB,WAAW,EAAY,MAAM,oBAAoB,CAAC;AAIlF,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE/C,OAAO,KAAK,IAAI,MAAM,uBAAuB,CAAC;AAE9C,2DAA2D;AAC3D,qBAAa,OAAO;IAClB;;;;;OAKG;WACU,MAAM,CAAC,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAY/E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAG7C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAG/C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAoC;IAGvE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAG7C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA8E;IAExG,OAAO;IAgBP,wDAAwD;IACxD,WAAW,IAAI,IAAI,CAAC,KAAK;IAiCzB,gDAAgD;IAChD,OAAO,CAAC,SAAS;IAajB,4DAA4D;IAC5D,OAAO,CAAC,UAAU;IAuBlB;;;;;OAKG;IACH,OAAO,CAAC,OAAO;IAoBf;;;;;;OAMG;IACH,OAAO,CAAC,YAAY;IAWpB;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAkEhB;;;;;;OAMG;IACH,OAAO,CAAC,aAAa;IA4BrB,0CAA0C;IAC1C,OAAO,CAAC,OAAO;IAkLf,yCAAyC;IACzC,OAAO,CAAC,cAAc;IAUtB,mCAAmC;IACnC,OAAO,CAAC,aAAa;IAWrB,iCAAiC;IACjC,OAAO,CAAC,WAAW;IAWnB;;;;;OAKG;IACH,OAAO,CAAC,UAAU;IAWlB,4EAA4E;IAC5E,OAAO,CAAC,YAAY;IASpB;;;;;;;;OAQG;IACH,OAAO,CAAC,SAAS;IAOjB;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IA+O5B;;;;;;OAMG;IACH,OAAO,CAAC,yBAAyB;IAgBjC;;;;;;OAMG;IACH,OAAO,CAAC,oBAAoB;IAwC5B;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IAwB3B;;;;;OAKG;IACH,OAAO,CAAC,WAAW;IAyNnB;;;;;;;;OAQG;IACH,OAAO,CAAC,yBAAyB;IAuDjC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAczB;;;;;OAKG;IACH,OAAO,CAAC,2BAA2B;IA6BnC;;;;;OAKG;IACH,OAAO,CAAC,oBAAoB;IA2G5B;;;;;;OAMG;IACH,OAAO,CAAC,cAAc;IAuBtB;;;;;;;OAOG;IACH,OAAO,CAAC,0BAA0B;IA2ClC;;;;;OAKG;IACH,OAAO,CAAC,eAAe;CAaxB"}
|
|
@@ -32,18 +32,21 @@ export class Adapter {
|
|
|
32
32
|
this.types = new Map();
|
|
33
33
|
this.clientMethodParams = new Map();
|
|
34
34
|
this.renamedMethods = new Set();
|
|
35
|
+
this.fieldsMap = new Map();
|
|
35
36
|
this.ctx = ctx;
|
|
37
|
+
this.options = options;
|
|
36
38
|
let serviceType = 'data-plane';
|
|
37
39
|
if (this.ctx.arm === true) {
|
|
38
40
|
serviceType = 'azure-arm';
|
|
39
41
|
}
|
|
40
|
-
this.crate = new rust.Crate(options['crate-name'], options['crate-version'], serviceType);
|
|
42
|
+
this.crate = new rust.Crate(this.options['crate-name'], this.options['crate-version'], serviceType);
|
|
41
43
|
}
|
|
42
44
|
/** performs all the steps to convert tcgc to a crate */
|
|
43
45
|
tcgcToCrate() {
|
|
44
46
|
this.adaptTypes();
|
|
45
47
|
this.adaptClients();
|
|
46
|
-
|
|
48
|
+
// marker models don't require serde so exclude them from the check
|
|
49
|
+
if (this.crate.enums.length > 0 || values(this.crate.models).where(e => e.kind === 'model').any()) {
|
|
47
50
|
this.crate.addDependency(new rust.CrateDependency('serde'));
|
|
48
51
|
}
|
|
49
52
|
if (this.crate.clients.length > 0 || this.crate.enums.length > 0 || this.crate.models.length > 0) {
|
|
@@ -220,7 +223,7 @@ export class Adapter {
|
|
|
220
223
|
// the only exception is for HashMap and Vec since an
|
|
221
224
|
// empty collection conveys the same semantics.
|
|
222
225
|
if ((modelVisibility === 'pub' || property.optional) && fieldType.kind !== 'hashmap' && fieldType.kind !== 'Vec') {
|
|
223
|
-
fieldType = new rust.Option(fieldType);
|
|
226
|
+
fieldType = new rust.Option(this.typeToWireType(fieldType));
|
|
224
227
|
}
|
|
225
228
|
const modelField = new rust.ModelField(naming.getEscapedReservedName(snakeCaseName(property.name), 'prop'), property.serializedName, modelVisibility, fieldType);
|
|
226
229
|
modelField.docs = this.adaptDocs(property.summary, property.doc);
|
|
@@ -230,6 +233,10 @@ export class Adapter {
|
|
|
230
233
|
modelField.serde = xmlName;
|
|
231
234
|
}
|
|
232
235
|
modelField.xmlKind = getXMLKind(property.decorators, modelField);
|
|
236
|
+
// it's possible for different models to reference the same property definition
|
|
237
|
+
if (!this.fieldsMap.get(property)) {
|
|
238
|
+
this.fieldsMap.set(property, modelField);
|
|
239
|
+
}
|
|
233
240
|
return modelField;
|
|
234
241
|
}
|
|
235
242
|
/** converts a tcgc type to a Rust type */
|
|
@@ -296,7 +303,7 @@ export class Adapter {
|
|
|
296
303
|
if (vectorType) {
|
|
297
304
|
return vectorType;
|
|
298
305
|
}
|
|
299
|
-
vectorType = new rust.Vector(this.getType(type.valueType));
|
|
306
|
+
vectorType = new rust.Vector(this.typeToWireType(this.getType(type.valueType)));
|
|
300
307
|
this.types.set(keyName, vectorType);
|
|
301
308
|
return vectorType;
|
|
302
309
|
}
|
|
@@ -322,7 +329,7 @@ export class Adapter {
|
|
|
322
329
|
if (hashmapType) {
|
|
323
330
|
return hashmapType;
|
|
324
331
|
}
|
|
325
|
-
hashmapType = new rust.HashMap(this.getType(type.valueType));
|
|
332
|
+
hashmapType = new rust.HashMap(this.typeToWireType(this.getType(type.valueType)));
|
|
326
333
|
this.types.set(keyName, hashmapType);
|
|
327
334
|
return hashmapType;
|
|
328
335
|
}
|
|
@@ -457,16 +464,27 @@ export class Adapter {
|
|
|
457
464
|
/** converts all tcgc clients and their methods into Rust clients/methods */
|
|
458
465
|
adaptClients() {
|
|
459
466
|
for (const client of this.ctx.sdkPackage.clients) {
|
|
460
|
-
if (client.methods.length === 0) {
|
|
461
|
-
// skip generating empty clients
|
|
462
|
-
continue;
|
|
463
|
-
}
|
|
464
467
|
// start with instantiable clients and recursively work down
|
|
465
468
|
if (client.clientInitialization.initializedBy & tcgc.InitializedByFlags.Individually) {
|
|
466
469
|
this.recursiveAdaptClient(client);
|
|
467
470
|
}
|
|
468
471
|
}
|
|
469
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* formats input as a doc link.
|
|
475
|
+
* e.g. [`${id}`](${link})
|
|
476
|
+
* if doc links are disabled, id is returned
|
|
477
|
+
*
|
|
478
|
+
* @param id the ID of the doc link
|
|
479
|
+
* @param link the target of the doc link
|
|
480
|
+
* @returns the doc link or id
|
|
481
|
+
*/
|
|
482
|
+
asDocLink(id, link) {
|
|
483
|
+
if (this.options['temp-omit-doc-links'] === true) {
|
|
484
|
+
return `\`${id}\``;
|
|
485
|
+
}
|
|
486
|
+
return `[\`${id}\`](${link})`;
|
|
487
|
+
}
|
|
470
488
|
/**
|
|
471
489
|
* recursively converts a client and its methods.
|
|
472
490
|
* this simplifies the case for hierarchical clients.
|
|
@@ -502,15 +520,16 @@ export class Adapter {
|
|
|
502
520
|
const rustClient = new rust.Client(clientName);
|
|
503
521
|
rustClient.docs = this.adaptDocs(client.summary, client.doc);
|
|
504
522
|
rustClient.parent = parent;
|
|
505
|
-
rustClient.fields.push(new rust.StructField('pipeline', 'pubCrate', new rust.ExternalType(this.crate, '
|
|
523
|
+
rustClient.fields.push(new rust.StructField('pipeline', 'pubCrate', new rust.ExternalType(this.crate, 'Pipeline', 'azure_core::http')));
|
|
506
524
|
// anything other than public means non-instantiable client
|
|
507
525
|
if (client.clientInitialization.initializedBy & tcgc.InitializedByFlags.Individually) {
|
|
508
526
|
const clientOptionsStruct = new rust.Struct(`${rustClient.name}Options`, 'pub');
|
|
509
|
-
const clientOptionsField = new rust.StructField('client_options', 'pub', new rust.ExternalType(this.crate, '
|
|
527
|
+
const clientOptionsField = new rust.StructField('client_options', 'pub', new rust.ExternalType(this.crate, 'ClientOptions', 'azure_core::http'));
|
|
528
|
+
clientOptionsField.docs.summary = 'Allows customization of the client.';
|
|
510
529
|
clientOptionsField.defaultValue = 'ClientOptions::default()';
|
|
511
530
|
clientOptionsStruct.fields.push(clientOptionsField);
|
|
512
531
|
rustClient.constructable = new rust.ClientConstruction(new rust.ClientOptions(clientOptionsStruct));
|
|
513
|
-
clientOptionsStruct.docs.summary = `Options used when creating a
|
|
532
|
+
clientOptionsStruct.docs.summary = `Options used when creating a ${this.asDocLink(rustClient.name, rustClient.name)}`;
|
|
514
533
|
// NOTE: per tcgc convention, if there is no param of kind credential
|
|
515
534
|
// it means that the client doesn't require any kind of authentication.
|
|
516
535
|
// HOWEVER, if there *is* a credential param, then the client *does not*
|
|
@@ -674,14 +693,23 @@ export class Adapter {
|
|
|
674
693
|
else {
|
|
675
694
|
throw new Error(`uninstantiable client ${client.name} has no parent`);
|
|
676
695
|
}
|
|
696
|
+
for (const child of values(client.children)) {
|
|
697
|
+
const subClient = this.recursiveAdaptClient(child, rustClient);
|
|
698
|
+
this.adaptClientAccessor(client, child, rustClient, subClient);
|
|
699
|
+
}
|
|
677
700
|
for (const method of client.methods) {
|
|
678
|
-
if (method.kind === '
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
701
|
+
if (method.kind === 'lro' || method.kind === 'lropaging') {
|
|
702
|
+
// skip LROs for now so that codegen is unblocked
|
|
703
|
+
// TODO: https://github.com/Azure/typespec-rust/issues/188
|
|
704
|
+
this.ctx.program.reportDiagnostic({
|
|
705
|
+
code: 'LroNotSupported',
|
|
706
|
+
severity: 'warning',
|
|
707
|
+
message: `skip emitting LRO ${method.name}`,
|
|
708
|
+
target: method.__raw ? method.__raw.node : NoTarget,
|
|
709
|
+
});
|
|
710
|
+
continue;
|
|
684
711
|
}
|
|
712
|
+
this.adaptMethod(method, rustClient);
|
|
685
713
|
}
|
|
686
714
|
this.crate.clients.push(rustClient);
|
|
687
715
|
return rustClient;
|
|
@@ -732,6 +760,7 @@ export class Adapter {
|
|
|
732
760
|
if (param.optional || param.clientDefaultValue) {
|
|
733
761
|
optional = true;
|
|
734
762
|
const paramField = new rust.StructField(paramName, 'pub', paramType);
|
|
763
|
+
paramField.docs = this.adaptDocs(param.summary, param.doc);
|
|
735
764
|
constructable.options.type.fields.push(paramField);
|
|
736
765
|
if (param.clientDefaultValue) {
|
|
737
766
|
paramField.defaultValue = `String::from("${param.clientDefaultValue}")`;
|
|
@@ -757,15 +786,15 @@ export class Adapter {
|
|
|
757
786
|
* @param rustClient the client to which the method belongs
|
|
758
787
|
* @param subClient the sub-client type that the method returns
|
|
759
788
|
*/
|
|
760
|
-
adaptClientAccessor(
|
|
789
|
+
adaptClientAccessor(parentClient, childClient, rustClient, subClient) {
|
|
761
790
|
const clientAccessor = new rust.ClientAccessor(`get_${snakeCaseName(subClient.name)}`, rustClient, subClient);
|
|
762
791
|
clientAccessor.docs.summary = `Returns a new instance of ${subClient.name}.`;
|
|
763
|
-
for (const param of
|
|
792
|
+
for (const param of childClient.clientInitialization.parameters) {
|
|
764
793
|
// check if the client's initializer already has this parameter.
|
|
765
794
|
// if it does then omit it from the method sig as we'll populate
|
|
766
795
|
// the child client's value from the parent.
|
|
767
796
|
let existsOnParent = false;
|
|
768
|
-
for (const clientParam of
|
|
797
|
+
for (const clientParam of parentClient.clientInitialization.parameters) {
|
|
769
798
|
if (clientParam.name === param.name) {
|
|
770
799
|
existsOnParent = true;
|
|
771
800
|
break;
|
|
@@ -807,8 +836,8 @@ export class Adapter {
|
|
|
807
836
|
const optionsLifetime = new rust.Lifetime('a');
|
|
808
837
|
const methodOptionsStruct = new rust.Struct(`${rustClient.name}${codegen.pascalCase(srcMethodName)}Options`, 'pub');
|
|
809
838
|
methodOptionsStruct.lifetime = optionsLifetime;
|
|
810
|
-
methodOptionsStruct.docs.summary = `Options to be passed to
|
|
811
|
-
const clientMethodOptions = new rust.ExternalType(this.crate, '
|
|
839
|
+
methodOptionsStruct.docs.summary = `Options to be passed to ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
|
|
840
|
+
const clientMethodOptions = new rust.ExternalType(this.crate, 'ClientMethodOptions', 'azure_core::http');
|
|
812
841
|
clientMethodOptions.lifetime = optionsLifetime;
|
|
813
842
|
const methodOptionsField = new rust.StructField('method_options', 'pub', clientMethodOptions);
|
|
814
843
|
methodOptionsField.docs.summary = 'Allows customization of the method call.';
|
|
@@ -834,18 +863,6 @@ export class Adapter {
|
|
|
834
863
|
break;
|
|
835
864
|
case 'paging':
|
|
836
865
|
rustMethod = new rust.PageableMethod(methodName, rustClient, pub, methodOptions, httpMethod, httpPath);
|
|
837
|
-
if (method.nextLinkOperation) {
|
|
838
|
-
// TODO: https://github.com/Azure/autorest.rust/issues/103
|
|
839
|
-
throw new Error('next page operation NYI');
|
|
840
|
-
}
|
|
841
|
-
else if (method.nextLinkPath) {
|
|
842
|
-
// this is the name of the field in the response type that contains the next link URL
|
|
843
|
-
if (method.nextLinkPath.indexOf('.') > 0) {
|
|
844
|
-
// TODO: https://github.com/Azure/autorest.rust/issues/102
|
|
845
|
-
throw new Error('nested next link path NYI');
|
|
846
|
-
}
|
|
847
|
-
rustMethod.strategy = new rust.PageableStrategyNextLink(naming.getEscapedReservedName(snakeCaseName(method.nextLinkPath), 'prop'));
|
|
848
|
-
}
|
|
849
866
|
break;
|
|
850
867
|
default:
|
|
851
868
|
throw new Error(`method kind ${method.kind} NYI`);
|
|
@@ -899,6 +916,13 @@ export class Adapter {
|
|
|
899
916
|
fieldType = field.type;
|
|
900
917
|
}
|
|
901
918
|
else {
|
|
919
|
+
switch (adaptedParam.type.kind) {
|
|
920
|
+
case 'requestContent':
|
|
921
|
+
case 'struct':
|
|
922
|
+
break;
|
|
923
|
+
default:
|
|
924
|
+
adaptedParam.type = this.typeToWireType(adaptedParam.type);
|
|
925
|
+
}
|
|
902
926
|
fieldType = new rust.Option(adaptedParam.type);
|
|
903
927
|
}
|
|
904
928
|
const optionsField = new rust.StructField(adaptedParam.name, 'pub', fieldType);
|
|
@@ -931,10 +955,10 @@ export class Adapter {
|
|
|
931
955
|
return this.adaptBodyFormat(defaultContentType);
|
|
932
956
|
};
|
|
933
957
|
// add any response headers
|
|
934
|
-
const
|
|
958
|
+
const responseHeaders = new Array();
|
|
935
959
|
for (const httpResp of method.operation.responses) {
|
|
936
960
|
for (const header of httpResp.headers) {
|
|
937
|
-
if (
|
|
961
|
+
if (responseHeaders.find((e) => e.serializedName === header.serializedName)) {
|
|
938
962
|
continue;
|
|
939
963
|
}
|
|
940
964
|
else if (header.type.kind === 'constant') {
|
|
@@ -947,19 +971,7 @@ export class Adapter {
|
|
|
947
971
|
// response headers if they need it.
|
|
948
972
|
continue;
|
|
949
973
|
}
|
|
950
|
-
|
|
951
|
-
if (header.type.kind === 'dict') {
|
|
952
|
-
if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
|
|
953
|
-
throw new Error(`unexpected response header collection ${header.serializedName}`);
|
|
954
|
-
}
|
|
955
|
-
responseHeader = new rust.ResponseHeaderHashMap(header.name, header.serializedName);
|
|
956
|
-
}
|
|
957
|
-
else {
|
|
958
|
-
responseHeader = new rust.ResponseHeaderScalar(fixETagName(header.name), fixETagName(header.serializedName), this.getType(header.type));
|
|
959
|
-
}
|
|
960
|
-
responseHeader.docs = this.adaptDocs(header.summary, header.doc);
|
|
961
|
-
rustMethod.responseHeaders.push(responseHeader);
|
|
962
|
-
addedHeaders.add(header.serializedName);
|
|
974
|
+
responseHeaders.push(header);
|
|
963
975
|
}
|
|
964
976
|
}
|
|
965
977
|
let returnType;
|
|
@@ -978,16 +990,16 @@ export class Adapter {
|
|
|
978
990
|
if (!this.crate.models.includes(synthesizedModel)) {
|
|
979
991
|
this.crate.models.push(synthesizedModel);
|
|
980
992
|
}
|
|
981
|
-
returnType = new rust.Pager(this.crate, synthesizedModel, format);
|
|
993
|
+
returnType = new rust.Pager(this.crate, new rust.Payload(synthesizedModel, format));
|
|
982
994
|
}
|
|
983
995
|
else if (method.response.type && !(method.response.type.kind === 'bytes' && method.response.type.encode === 'bytes')) {
|
|
984
|
-
returnType = new rust.Response(this.crate, new rust.Payload(this.getType(method.response.type), getBodyFormat()));
|
|
996
|
+
returnType = new rust.Response(this.crate, new rust.Payload(this.typeToWireType(this.getType(method.response.type)), getBodyFormat()));
|
|
985
997
|
}
|
|
986
|
-
else if (
|
|
998
|
+
else if (responseHeaders.length > 0) {
|
|
987
999
|
// for methods that don't return a modeled type but return headers,
|
|
988
1000
|
// we need to return a marker type
|
|
989
1001
|
const markerType = new rust.MarkerType(`${rustClient.name}${codegen.pascalCase(method.name)}Result`);
|
|
990
|
-
markerType.docs.summary = `Contains results for
|
|
1002
|
+
markerType.docs.summary = `Contains results for ${this.asDocLink(`${rustClient.name}::${methodName}()`, `crate::generated::clients::${rustClient.name}::${methodName}()`)}`;
|
|
991
1003
|
returnType = new rust.Response(this.crate, markerType);
|
|
992
1004
|
this.crate.models.push(markerType);
|
|
993
1005
|
}
|
|
@@ -999,6 +1011,125 @@ export class Adapter {
|
|
|
999
1011
|
returnType = new rust.Response(this.crate, this.getUnitType());
|
|
1000
1012
|
}
|
|
1001
1013
|
rustMethod.returns = new rust.Result(this.crate, returnType);
|
|
1014
|
+
rustMethod.responseHeaders = this.adaptResponseHeadersTrait(rustClient, rustMethod, responseHeaders);
|
|
1015
|
+
if (method.kind === 'paging') {
|
|
1016
|
+
// can't do this until the method has been completely adapted
|
|
1017
|
+
rustMethod.strategy = this.adaptPageableMethodStrategy(method);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
/**
|
|
1021
|
+
* adapts response headers into a Rust ResponseHeadersTrait
|
|
1022
|
+
* if there are no response headers, undefined is returned.
|
|
1023
|
+
*
|
|
1024
|
+
* @param client the client that contains the method
|
|
1025
|
+
* @param method the method for which to create the trait
|
|
1026
|
+
* @param responseHeaders the response headers array (can be empty)
|
|
1027
|
+
* @returns a ResponseHeadersTrait or undefined
|
|
1028
|
+
*/
|
|
1029
|
+
adaptResponseHeadersTrait(client, method, responseHeaders) {
|
|
1030
|
+
if (responseHeaders.length === 0) {
|
|
1031
|
+
return undefined;
|
|
1032
|
+
}
|
|
1033
|
+
// response header traits are only ever for marker types and payloads
|
|
1034
|
+
let implFor;
|
|
1035
|
+
switch (method.returns.type.kind) {
|
|
1036
|
+
case 'pager':
|
|
1037
|
+
implFor = method.returns.type.type;
|
|
1038
|
+
break;
|
|
1039
|
+
case 'response':
|
|
1040
|
+
if (method.returns.type.content.kind !== 'marker' && method.returns.type.content.kind !== 'payload') {
|
|
1041
|
+
throw new Error(`unexpected method content kind ${method.returns.type.content.kind}`);
|
|
1042
|
+
}
|
|
1043
|
+
implFor = method.returns.type.content;
|
|
1044
|
+
break;
|
|
1045
|
+
default:
|
|
1046
|
+
// this is Unit which should have been previously skipped
|
|
1047
|
+
throw new Error(`unexpected method return kind ${method.returns.type.kind}`);
|
|
1048
|
+
}
|
|
1049
|
+
let traitName;
|
|
1050
|
+
switch (implFor.kind) {
|
|
1051
|
+
case 'marker':
|
|
1052
|
+
traitName = implFor.name;
|
|
1053
|
+
break;
|
|
1054
|
+
case 'payload':
|
|
1055
|
+
traitName = this.recursiveTypeName(implFor.type);
|
|
1056
|
+
break;
|
|
1057
|
+
}
|
|
1058
|
+
traitName += 'Headers';
|
|
1059
|
+
// NOTE: the complete doc text will be emitted at codegen time
|
|
1060
|
+
const docs = this.asDocLink(`${client.name}::${method.name}()`, `crate::generated::clients::${client.name}::${method.name}()`);
|
|
1061
|
+
const responseHeadersTrait = new rust.ResponseHeadersTrait(traitName, implFor, docs);
|
|
1062
|
+
// adapt the response headers and add them to the trait
|
|
1063
|
+
for (const header of responseHeaders) {
|
|
1064
|
+
let responseHeader;
|
|
1065
|
+
if (header.type.kind === 'dict') {
|
|
1066
|
+
if (header.serializedName !== 'x-ms-meta' && header.serializedName !== 'x-ms-or') {
|
|
1067
|
+
throw new Error(`unexpected response header collection ${header.serializedName}`);
|
|
1068
|
+
}
|
|
1069
|
+
responseHeader = new rust.ResponseHeaderHashMap(snakeCaseName(header.name), header.serializedName);
|
|
1070
|
+
}
|
|
1071
|
+
else {
|
|
1072
|
+
responseHeader = new rust.ResponseHeaderScalar(snakeCaseName(header.name), fixETagName(header.serializedName), this.getType(header.type));
|
|
1073
|
+
}
|
|
1074
|
+
responseHeader.docs = this.adaptDocs(header.summary, header.doc);
|
|
1075
|
+
responseHeadersTrait.headers.push(responseHeader);
|
|
1076
|
+
}
|
|
1077
|
+
return responseHeadersTrait;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* recursively builds a name from the specified type.
|
|
1081
|
+
* e.g. Vec<FooModel> would be VecFooModel etc.
|
|
1082
|
+
*
|
|
1083
|
+
* @param type the type for which to build a name
|
|
1084
|
+
* @returns the name
|
|
1085
|
+
*/
|
|
1086
|
+
recursiveTypeName(type) {
|
|
1087
|
+
switch (type.kind) {
|
|
1088
|
+
case 'enum':
|
|
1089
|
+
case 'model':
|
|
1090
|
+
return type.name;
|
|
1091
|
+
case 'hashmap':
|
|
1092
|
+
return `${type.name}${this.recursiveTypeName(type.type)}`;
|
|
1093
|
+
case 'Vec':
|
|
1094
|
+
return `${type.kind}${this.recursiveTypeName(type.type)}`;
|
|
1095
|
+
default:
|
|
1096
|
+
return codegen.capitalize(type.kind);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
/**
|
|
1100
|
+
* creates the pageable strategy based on the method definition
|
|
1101
|
+
*
|
|
1102
|
+
* @param method the pageable method for which to create a strategy
|
|
1103
|
+
* @returns the pageable strategy
|
|
1104
|
+
*/
|
|
1105
|
+
adaptPageableMethodStrategy(method) {
|
|
1106
|
+
if (method.pagingMetadata.nextLinkOperation) {
|
|
1107
|
+
// TODO: https://github.com/Azure/autorest.rust/issues/103
|
|
1108
|
+
throw new Error('next page operation NYI');
|
|
1109
|
+
}
|
|
1110
|
+
else if (method.pagingMetadata.nextLinkSegments) {
|
|
1111
|
+
if (method.pagingMetadata.nextLinkSegments.length > 1) {
|
|
1112
|
+
// TODO: https://github.com/Azure/autorest.rust/issues/102
|
|
1113
|
+
throw new Error('nested next link path NYI');
|
|
1114
|
+
}
|
|
1115
|
+
// this is the field in the response type that contains the next link URL
|
|
1116
|
+
const nextLinkSegment = method.pagingMetadata.nextLinkSegments[0];
|
|
1117
|
+
if (nextLinkSegment.kind !== 'property') {
|
|
1118
|
+
throw new Error(`unexpected kind ${nextLinkSegment.kind} for next link segment in operation ${method.name}`);
|
|
1119
|
+
}
|
|
1120
|
+
const nextLinkField = this.fieldsMap.get(nextLinkSegment);
|
|
1121
|
+
if (!nextLinkField) {
|
|
1122
|
+
// the most likely explanation for this is lack of reference equality
|
|
1123
|
+
throw new Error(`missing next link field name ${nextLinkSegment.name} for operation ${method.name}`);
|
|
1124
|
+
}
|
|
1125
|
+
return new rust.PageableStrategyNextLink(nextLinkField);
|
|
1126
|
+
}
|
|
1127
|
+
else if (method.pagingMetadata.continuationTokenParameterSegments && method.pagingMetadata.continuationTokenResponseSegments) {
|
|
1128
|
+
throw new Error('PageableStrategyContinuationToken NYI');
|
|
1129
|
+
}
|
|
1130
|
+
else {
|
|
1131
|
+
throw new Error(`unhandled paging strategy for operation ${method.name}`);
|
|
1132
|
+
}
|
|
1002
1133
|
}
|
|
1003
1134
|
/**
|
|
1004
1135
|
* converts a tcgc operation parameter into a Rust method parameter
|
|
@@ -1042,7 +1173,7 @@ export class Adapter {
|
|
|
1042
1173
|
requestType = new rust.Bytes(this.crate);
|
|
1043
1174
|
}
|
|
1044
1175
|
else {
|
|
1045
|
-
requestType = new rust.Payload(paramType, this.adaptBodyFormat(param.defaultContentType));
|
|
1176
|
+
requestType = new rust.Payload(this.typeToWireType(paramType), this.adaptBodyFormat(param.defaultContentType));
|
|
1046
1177
|
}
|
|
1047
1178
|
adaptedParam = new rust.BodyParameter(paramName, paramLoc, param.optional, new rust.RequestContent(this.crate, requestType));
|
|
1048
1179
|
break;
|
|
@@ -1108,6 +1239,35 @@ export class Adapter {
|
|
|
1108
1239
|
}
|
|
1109
1240
|
return adaptedParam;
|
|
1110
1241
|
}
|
|
1242
|
+
/**
|
|
1243
|
+
* narrows a rust.Type to a rust.WireType.
|
|
1244
|
+
* if type isn't a rust.WireType, an error is thrown.
|
|
1245
|
+
*
|
|
1246
|
+
* @param type the type to narrow
|
|
1247
|
+
* @returns the narrowed type
|
|
1248
|
+
*/
|
|
1249
|
+
typeToWireType(type) {
|
|
1250
|
+
switch (type.kind) {
|
|
1251
|
+
case 'bytes':
|
|
1252
|
+
case 'encodedBytes':
|
|
1253
|
+
case 'enum':
|
|
1254
|
+
case 'enumValue':
|
|
1255
|
+
case 'Etag':
|
|
1256
|
+
case 'hashmap':
|
|
1257
|
+
case 'jsonValue':
|
|
1258
|
+
case 'literal':
|
|
1259
|
+
case 'model':
|
|
1260
|
+
case 'offsetDateTime':
|
|
1261
|
+
case 'scalar':
|
|
1262
|
+
case 'str':
|
|
1263
|
+
case 'String':
|
|
1264
|
+
case 'Url':
|
|
1265
|
+
case 'Vec':
|
|
1266
|
+
return type;
|
|
1267
|
+
default:
|
|
1268
|
+
throw new Error(`cannot convert ${type.kind} to a wire type`);
|
|
1269
|
+
}
|
|
1270
|
+
}
|
|
1111
1271
|
/**
|
|
1112
1272
|
* converts a tcgc spread parameter into a Rust partial body parameter.
|
|
1113
1273
|
*
|