@azure-tools/typespec-rust 0.11.0 → 0.12.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 +8 -13
- package/dist/src/codegen/clients.d.ts.map +1 -1
- package/dist/src/codegen/clients.js +38 -32
- package/dist/src/codegen/clients.js.map +1 -1
- package/dist/src/codegen/codeGenerator.d.ts +1 -2
- package/dist/src/codegen/codeGenerator.d.ts.map +1 -1
- package/dist/src/codegen/codeGenerator.js +28 -27
- package/dist/src/codegen/codeGenerator.js.map +1 -1
- package/dist/src/codegen/enums.d.ts +2 -1
- package/dist/src/codegen/enums.d.ts.map +1 -1
- package/dist/src/codegen/enums.js +4 -1
- 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 +6 -4
- package/dist/src/codegen/headerTraits.js.map +1 -1
- package/dist/src/codegen/helpers.d.ts +11 -4
- package/dist/src/codegen/helpers.d.ts.map +1 -1
- package/dist/src/codegen/helpers.js +13 -8
- package/dist/src/codegen/helpers.js.map +1 -1
- package/dist/src/codegen/lib.d.ts +1 -4
- package/dist/src/codegen/lib.d.ts.map +1 -1
- package/dist/src/codegen/lib.js +4 -99
- package/dist/src/codegen/lib.js.map +1 -1
- package/dist/src/codegen/mod.d.ts +10 -4
- package/dist/src/codegen/mod.d.ts.map +1 -1
- package/dist/src/codegen/mod.js +36 -19
- package/dist/src/codegen/mod.js.map +1 -1
- package/dist/src/codegen/models.d.ts +5 -4
- package/dist/src/codegen/models.d.ts.map +1 -1
- package/dist/src/codegen/models.js +453 -46
- package/dist/src/codegen/models.js.map +1 -1
- package/dist/src/codegen/use.d.ts +7 -6
- package/dist/src/codegen/use.d.ts.map +1 -1
- package/dist/src/codegen/use.js +31 -16
- package/dist/src/codegen/use.js.map +1 -1
- package/dist/src/codemodel/client.d.ts +8 -3
- package/dist/src/codemodel/client.d.ts.map +1 -1
- package/dist/src/codemodel/client.js +9 -8
- package/dist/src/codemodel/client.js.map +1 -1
- package/dist/src/codemodel/method.d.ts +3 -3
- package/dist/src/codemodel/method.d.ts.map +1 -1
- package/dist/src/codemodel/method.js +2 -2
- package/dist/src/codemodel/method.js.map +1 -1
- package/dist/src/codemodel/types.d.ts +13 -11
- package/dist/src/codemodel/types.d.ts.map +1 -1
- package/dist/src/codemodel/types.js +15 -15
- package/dist/src/codemodel/types.js.map +1 -1
- package/dist/src/emitter.d.ts.map +1 -1
- package/dist/src/emitter.js +12 -6
- 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 +2 -1
- package/dist/src/lib.js.map +1 -1
- package/dist/src/tcgcadapter/adapter.d.ts +3 -1
- package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
- package/dist/src/tcgcadapter/adapter.js +60 -36
- package/dist/src/tcgcadapter/adapter.js.map +1 -1
- package/package.json +23 -23
|
@@ -2,6 +2,7 @@
|
|
|
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
|
+
import * as codegen from '@azure-tools/codegen';
|
|
5
6
|
import * as helpers from './helpers.js';
|
|
6
7
|
import { Use } from './use.js';
|
|
7
8
|
import * as rust from '../codemodel/index.js';
|
|
@@ -18,9 +19,9 @@ export function emitModels(crate, context) {
|
|
|
18
19
|
return {};
|
|
19
20
|
}
|
|
20
21
|
return {
|
|
21
|
-
public: emitModelsInternal(crate, context,
|
|
22
|
+
public: emitModelsInternal(crate, context, 'pub'),
|
|
22
23
|
serde: emitModelsSerde(crate, context),
|
|
23
|
-
internal: emitModelsInternal(crate, context,
|
|
24
|
+
internal: emitModelsInternal(crate, context, 'pubCrate'),
|
|
24
25
|
xmlHelpers: emitXMLListWrappers(),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
@@ -29,18 +30,18 @@ export function emitModels(crate, context) {
|
|
|
29
30
|
*
|
|
30
31
|
* @param crate the crate for which to emit models
|
|
31
32
|
* @param context the context for the provided crate
|
|
32
|
-
* @param
|
|
33
|
+
* @param visibility the visibility of the models to emit
|
|
33
34
|
* @returns the model content or empty
|
|
34
35
|
*/
|
|
35
|
-
function emitModelsInternal(crate, context,
|
|
36
|
+
function emitModelsInternal(crate, context, visibility) {
|
|
36
37
|
// for the internal models we might need to use public model types
|
|
37
|
-
const use = new Use(pub ? 'models' :
|
|
38
|
+
const use = new Use(visibility === 'pub' ? 'models' : 'modelsOther');
|
|
38
39
|
use.addTypes('serde', ['Deserialize', 'Serialize']);
|
|
39
40
|
use.addType('typespec_client_core::fmt', 'SafeDebug');
|
|
40
41
|
const indent = new helpers.indentation();
|
|
41
42
|
let body = '';
|
|
42
43
|
for (const model of crate.models) {
|
|
43
|
-
if (pub && model.kind === 'marker') {
|
|
44
|
+
if (visibility === 'pub' && model.kind === 'marker') {
|
|
44
45
|
body += helpers.formatDocComment(model.docs);
|
|
45
46
|
// marker types don't have any fields
|
|
46
47
|
// and don't participate in serde.
|
|
@@ -52,7 +53,7 @@ function emitModelsInternal(crate, context, pub) {
|
|
|
52
53
|
// marker types are always public, so we skip them for the internal models file
|
|
53
54
|
continue;
|
|
54
55
|
}
|
|
55
|
-
if (model.
|
|
56
|
+
if (model.visibility !== visibility) {
|
|
56
57
|
continue;
|
|
57
58
|
}
|
|
58
59
|
const bodyFormat = context.getModelBodyFormat(model);
|
|
@@ -69,7 +70,7 @@ function emitModelsInternal(crate, context, pub) {
|
|
|
69
70
|
// json is the default if not specified so no need to handle it
|
|
70
71
|
body += `#[typespec(format = "xml")]\n`;
|
|
71
72
|
}
|
|
72
|
-
body +=
|
|
73
|
+
body += `${helpers.emitVisibility(model.visibility)}struct ${model.name} {\n`;
|
|
73
74
|
for (const field of model.fields) {
|
|
74
75
|
use.addForType(field.type);
|
|
75
76
|
body += helpers.formatDocComment(field.docs);
|
|
@@ -80,35 +81,16 @@ function emitModelsInternal(crate, context, pub) {
|
|
|
80
81
|
}
|
|
81
82
|
// NOTE: usage of serde annotations like this means that base64 encoded bytes and
|
|
82
83
|
// XML wrapped lists are mutually exclusive. it's not a real scenario at present.
|
|
83
|
-
if (helpers.
|
|
84
|
-
serdeParams
|
|
85
|
-
let optionMod = '';
|
|
86
|
-
if (field.type.kind === 'option') {
|
|
87
|
-
optionMod = '::option';
|
|
88
|
-
}
|
|
89
|
-
// TODO: https://github.com/Azure/typespec-rust/issues/221
|
|
90
|
-
// specifically need to handle nested arrays of time types that require conversion
|
|
91
|
-
serdeParams.add(`with = "azure_core::date::${helpers.unwrapOption(field.type).encoding}${optionMod}"`);
|
|
92
|
-
}
|
|
93
|
-
else if (helpers.unwrapOption(field.type).kind === 'encodedBytes') {
|
|
94
|
-
// TODO: https://github.com/Azure/typespec-rust/issues/221
|
|
95
|
-
// specifically need to handle nested arrays of base64 encoded bytes
|
|
96
|
-
let format = '';
|
|
97
|
-
if (helpers.unwrapOption(field.type).encoding === 'url') {
|
|
98
|
-
format = '_url_safe';
|
|
99
|
-
}
|
|
100
|
-
serdeParams.add('default');
|
|
101
|
-
serdeParams.add(`deserialize_with = "base64::deserialize${format}"`);
|
|
102
|
-
serdeParams.add(`serialize_with = "base64::serialize${format}"`);
|
|
103
|
-
use.addType('azure_core', 'base64');
|
|
84
|
+
if (helpers.unwrapType(field.type).kind === 'encodedBytes' || helpers.unwrapType(field.type).kind === 'offsetDateTime') {
|
|
85
|
+
getSerDeHelper(field.type, serdeParams, use);
|
|
104
86
|
}
|
|
105
|
-
else if (bodyFormat === 'xml' && helpers.unwrapOption(field.type).kind === '
|
|
87
|
+
else if (bodyFormat === 'xml' && helpers.unwrapOption(field.type).kind === 'Vec' && field.xmlKind !== 'unwrappedList') {
|
|
106
88
|
// this is a wrapped list so we need a helper type for serde
|
|
107
89
|
const xmlListWrapper = getXMLListWrapper(field);
|
|
108
90
|
serdeParams.add('default');
|
|
109
91
|
serdeParams.add(`deserialize_with = "${xmlListWrapper.name}::unwrap"`);
|
|
110
92
|
serdeParams.add(`serialize_with = "${xmlListWrapper.name}::wrap"`);
|
|
111
|
-
use.addType('
|
|
93
|
+
use.addType('super::xml_helpers', xmlListWrapper.name);
|
|
112
94
|
}
|
|
113
95
|
// TODO: omit skip_serializing_if if we need to send explicit JSON null
|
|
114
96
|
// https://github.com/Azure/typespec-rust/issues/78
|
|
@@ -119,17 +101,17 @@ function emitModelsInternal(crate, context, pub) {
|
|
|
119
101
|
serdeParams.add('default');
|
|
120
102
|
serdeParams.add('skip_serializing_if = "HashMap::is_empty"');
|
|
121
103
|
}
|
|
122
|
-
else if (field.type.kind === '
|
|
104
|
+
else if (field.type.kind === 'Vec') {
|
|
123
105
|
serdeParams.add('default');
|
|
124
|
-
// NOTE: we want to send an empty
|
|
125
|
-
if (bodyFormat !== 'xml') {
|
|
106
|
+
// NOTE: we want to send an empty wrapper element for wrapped arrays
|
|
107
|
+
if (bodyFormat !== 'xml' || field.xmlKind === 'unwrappedList') {
|
|
126
108
|
serdeParams.add('skip_serializing_if = "Vec::is_empty"');
|
|
127
109
|
}
|
|
128
110
|
}
|
|
129
111
|
if (serdeParams.size > 0) {
|
|
130
112
|
body += `${indent.get()}#[serde(${Array.from(serdeParams).sort().join(', ')})]\n`;
|
|
131
113
|
}
|
|
132
|
-
body += `${indent.get()}${helpers.
|
|
114
|
+
body += `${indent.get()}${helpers.emitVisibility(field.visibility)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n\n`;
|
|
133
115
|
}
|
|
134
116
|
body += '}\n\n';
|
|
135
117
|
}
|
|
@@ -139,9 +121,9 @@ function emitModelsInternal(crate, context, pub) {
|
|
|
139
121
|
}
|
|
140
122
|
// emit TryFrom as required for internal models only.
|
|
141
123
|
// public models will have their helpers in a separate file.
|
|
142
|
-
if (
|
|
124
|
+
if (visibility !== 'pub') {
|
|
143
125
|
for (const model of crate.models) {
|
|
144
|
-
if (model.kind === 'marker' ||
|
|
126
|
+
if (model.kind === 'marker' || model.visibility === 'pub') {
|
|
145
127
|
continue;
|
|
146
128
|
}
|
|
147
129
|
body += context.getTryFromForRequestContent(model, use);
|
|
@@ -150,7 +132,10 @@ function emitModelsInternal(crate, context, pub) {
|
|
|
150
132
|
let content = helpers.contentPreamble();
|
|
151
133
|
content += use.text();
|
|
152
134
|
content += body;
|
|
153
|
-
return
|
|
135
|
+
return {
|
|
136
|
+
name: visibility === 'pub' ? 'pub_models' : 'crate_models',
|
|
137
|
+
content: content,
|
|
138
|
+
};
|
|
154
139
|
}
|
|
155
140
|
/**
|
|
156
141
|
* returns serde helpers for public models.
|
|
@@ -161,11 +146,11 @@ function emitModelsInternal(crate, context, pub) {
|
|
|
161
146
|
* @returns the model serde helpers content or undefined
|
|
162
147
|
*/
|
|
163
148
|
function emitModelsSerde(crate, context) {
|
|
164
|
-
const use = new Use();
|
|
149
|
+
const use = new Use('modelsOther');
|
|
165
150
|
let body = '';
|
|
166
151
|
// emit TryFrom as required
|
|
167
152
|
for (const model of crate.models) {
|
|
168
|
-
if (model.kind === 'marker' || model.
|
|
153
|
+
if (model.kind === 'marker' || model.visibility !== 'pub') {
|
|
169
154
|
// skip internal models as their serde helpers are in the same file
|
|
170
155
|
continue;
|
|
171
156
|
}
|
|
@@ -177,14 +162,23 @@ function emitModelsSerde(crate, context) {
|
|
|
177
162
|
}
|
|
178
163
|
body += forReq;
|
|
179
164
|
}
|
|
180
|
-
|
|
165
|
+
const serdeHelpers = emitSerDeHelpers();
|
|
166
|
+
if (body.length === 0 && !serdeHelpers) {
|
|
181
167
|
// no helpers
|
|
182
168
|
return undefined;
|
|
183
169
|
}
|
|
184
170
|
let content = helpers.contentPreamble();
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
171
|
+
if (body.length > 0) {
|
|
172
|
+
content += use.text();
|
|
173
|
+
content += body;
|
|
174
|
+
}
|
|
175
|
+
if (serdeHelpers) {
|
|
176
|
+
content += serdeHelpers;
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
name: 'models_serde',
|
|
180
|
+
content: content,
|
|
181
|
+
};
|
|
188
182
|
}
|
|
189
183
|
/**
|
|
190
184
|
* returns the value for the rename option in a serde derive macro
|
|
@@ -297,7 +291,7 @@ function emitXMLListWrappers() {
|
|
|
297
291
|
const wrapperTypes = Array.from(xmlListWrappers.values());
|
|
298
292
|
wrapperTypes.sort((a, b) => { return helpers.sortAscending(a.name, b.name); });
|
|
299
293
|
const indent = new helpers.indentation();
|
|
300
|
-
const use = new Use();
|
|
294
|
+
const use = new Use('modelsOther');
|
|
301
295
|
use.addTypes('serde', ['Deserialize', 'Deserializer', 'Serialize', 'Serializer']);
|
|
302
296
|
let body = '';
|
|
303
297
|
for (const wrapperType of wrapperTypes) {
|
|
@@ -307,7 +301,7 @@ function emitXMLListWrappers() {
|
|
|
307
301
|
}
|
|
308
302
|
use.addForType(wrapperType.fieldType);
|
|
309
303
|
const fieldType = helpers.getTypeDeclaration(wrapperType.fieldType);
|
|
310
|
-
body += `pub struct ${wrapperType.name} {\n`;
|
|
304
|
+
body += `pub(crate) struct ${wrapperType.name} {\n`;
|
|
311
305
|
body += `${indent.get()}#[serde(default)]\n`;
|
|
312
306
|
body += `${indent.get()}${wrapperType.fieldName}: ${fieldType},\n`;
|
|
313
307
|
body += '}\n\n';
|
|
@@ -330,6 +324,419 @@ function emitXMLListWrappers() {
|
|
|
330
324
|
content += '#![allow(non_camel_case_types)]\n#![allow(non_snake_case)]\n\n';
|
|
331
325
|
content += use.text();
|
|
332
326
|
content += body;
|
|
327
|
+
return {
|
|
328
|
+
name: 'xml_helpers',
|
|
329
|
+
content: content,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
333
|
+
// serde builder infrastructure
|
|
334
|
+
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
335
|
+
// used by getSerDeHelper and emitSerDeHelpers
|
|
336
|
+
const serdeHelpers = new Map();
|
|
337
|
+
/**
|
|
338
|
+
* defines serde helpers for encodedBytes and offsetDateTime types.
|
|
339
|
+
* any other type will cause this function to throw.
|
|
340
|
+
*
|
|
341
|
+
* @param type the encodedBytes/offsetDateTime type for which to build serde helpers
|
|
342
|
+
* @param serdeParams the params that will be passed to the serde annotation
|
|
343
|
+
* @param use the use statement builder currently in scope
|
|
344
|
+
*/
|
|
345
|
+
function getSerDeHelper(type, serdeParams, use) {
|
|
346
|
+
const unwrapped = helpers.unwrapType(type);
|
|
347
|
+
switch (unwrapped.kind) {
|
|
348
|
+
case 'encodedBytes':
|
|
349
|
+
break;
|
|
350
|
+
case 'offsetDateTime':
|
|
351
|
+
break;
|
|
352
|
+
default:
|
|
353
|
+
throw new Error(`getSerDeHelper unexpected kind ${type.kind}`);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* for hash maps and vectors, we emit a module containing the necessary
|
|
357
|
+
* helper functions to be used in a serde "with = <module>" statement.
|
|
358
|
+
* the module names are a concatenation of the type names.
|
|
359
|
+
* e.g. vec_offset_date_time, hashmap_vec_encoded_bytes_std etc
|
|
360
|
+
*/
|
|
361
|
+
const buildSerDeModName = function (type) {
|
|
362
|
+
let name = codegen.deconstruct(type.kind).join('_');
|
|
363
|
+
let unwrapped = type;
|
|
364
|
+
while (unwrapped.kind === 'hashmap' || unwrapped.kind === 'Vec') {
|
|
365
|
+
unwrapped = unwrapped.type;
|
|
366
|
+
name += '_' + codegen.deconstruct(unwrapped.kind).join('_');
|
|
367
|
+
}
|
|
368
|
+
switch (unwrapped.kind) {
|
|
369
|
+
case 'encodedBytes':
|
|
370
|
+
case 'offsetDateTime':
|
|
371
|
+
name += `_${unwrapped.encoding}`;
|
|
372
|
+
break;
|
|
373
|
+
default:
|
|
374
|
+
throw new Error(`unexpected kind ${unwrapped.kind}`);
|
|
375
|
+
}
|
|
376
|
+
// we can reuse identical helpers across model types
|
|
377
|
+
if (!serdeHelpers.has(name)) {
|
|
378
|
+
serdeHelpers.set(name, type);
|
|
379
|
+
}
|
|
380
|
+
return name;
|
|
381
|
+
};
|
|
382
|
+
/** non-collection based impl */
|
|
383
|
+
const serdeEncodedBytes = function (encoding) {
|
|
384
|
+
const format = encoding === 'url' ? '_url_safe' : '';
|
|
385
|
+
serdeParams.add('default');
|
|
386
|
+
serdeParams.add(`deserialize_with = "base64::deserialize${format}"`);
|
|
387
|
+
serdeParams.add(`serialize_with = "base64::serialize${format}"`);
|
|
388
|
+
use.addType('azure_core', 'base64');
|
|
389
|
+
};
|
|
390
|
+
/** non-collection based impl */
|
|
391
|
+
const serdeOffsetDateTime = function (encoding, optional) {
|
|
392
|
+
serdeParams.add('default');
|
|
393
|
+
serdeParams.add(`with = "azure_core::date::${encoding}${optional ? '::option' : ''}"`);
|
|
394
|
+
};
|
|
395
|
+
switch (type.kind) {
|
|
396
|
+
case 'encodedBytes':
|
|
397
|
+
return serdeEncodedBytes(unwrapped.encoding);
|
|
398
|
+
case 'offsetDateTime':
|
|
399
|
+
// this is for spread params where the internal model's field isn't Option<T>
|
|
400
|
+
return serdeOffsetDateTime(unwrapped.encoding, false);
|
|
401
|
+
case 'hashmap':
|
|
402
|
+
case 'Vec':
|
|
403
|
+
use.addType('super', 'models_serde');
|
|
404
|
+
serdeParams.add('default');
|
|
405
|
+
serdeParams.add(`with = "models_serde::${buildSerDeModName(type)}"`);
|
|
406
|
+
break;
|
|
407
|
+
case 'option':
|
|
408
|
+
switch (type.type.kind) {
|
|
409
|
+
case 'encodedBytes':
|
|
410
|
+
// TODO: this case should go away
|
|
411
|
+
return serdeEncodedBytes(unwrapped.encoding);
|
|
412
|
+
case 'offsetDateTime':
|
|
413
|
+
return serdeOffsetDateTime(unwrapped.encoding, true);
|
|
414
|
+
}
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* emits serde helper modules or returns undefined
|
|
420
|
+
* if no serde helpers are required.
|
|
421
|
+
*
|
|
422
|
+
* @returns the helper modules or undefined
|
|
423
|
+
*/
|
|
424
|
+
function emitSerDeHelpers() {
|
|
425
|
+
if (serdeHelpers.size === 0) {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
let content = '';
|
|
429
|
+
const helperKeys = Array.from(serdeHelpers.keys()).sort();
|
|
430
|
+
for (const helperKey of helperKeys) {
|
|
431
|
+
const use = new Use('modelsOther');
|
|
432
|
+
const indent = new helpers.indentation();
|
|
433
|
+
let modContent = `pub mod ${helperKey} {\n`;
|
|
434
|
+
modContent += `${indent.get()}#![allow(clippy::type_complexity)]\n`;
|
|
435
|
+
const deserialize = buildDeserialize(indent, serdeHelpers.get(helperKey), use);
|
|
436
|
+
const serialize = buildSerialize(indent, serdeHelpers.get(helperKey), use);
|
|
437
|
+
modContent += use.text(indent);
|
|
438
|
+
modContent += `${deserialize}\n${serialize}`;
|
|
439
|
+
modContent += '}\n\n'; // end pub mod
|
|
440
|
+
content += modContent;
|
|
441
|
+
}
|
|
442
|
+
return content;
|
|
443
|
+
}
|
|
444
|
+
/**
|
|
445
|
+
* constructs a serde deserialize function
|
|
446
|
+
*
|
|
447
|
+
* @param indent the indentation helper currently in scope
|
|
448
|
+
* @param type the type for which to build the helper
|
|
449
|
+
* @param use the use statement builder currently in scope
|
|
450
|
+
* @returns the pub fn deserialize function definition
|
|
451
|
+
*/
|
|
452
|
+
function buildDeserialize(indent, type, use) {
|
|
453
|
+
use.addTypes('serde', ['Deserialize', 'Deserializer']);
|
|
454
|
+
use.addType('std', 'result::Result');
|
|
455
|
+
use.addForType(type);
|
|
456
|
+
let content = `${indent.get()}pub fn deserialize<'de, D>(deserializer: D) -> Result<${helpers.getTypeDeclaration(type)}, D::Error>\n`;
|
|
457
|
+
content += `${indent.get()}where D: Deserializer<'de>\n${indent.get()}{\n`;
|
|
458
|
+
content += `${indent.push().get()}let to_deserialize = <Option<${getSerDeTypeDeclaration(type, 'deserialize')}>>::deserialize(deserializer)?;\n`;
|
|
459
|
+
content += `${indent.get()}${helpers.buildMatch(indent, 'to_deserialize', [
|
|
460
|
+
{
|
|
461
|
+
pattern: 'Some(to_deserialize)',
|
|
462
|
+
body: (indent) => recursiveBuildDeserializeBody(indent, use, {
|
|
463
|
+
caller: 'start',
|
|
464
|
+
type: type,
|
|
465
|
+
srcVar: 'to_deserialize',
|
|
466
|
+
}),
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
pattern: 'None',
|
|
470
|
+
body: (indent) => `${indent.get()}Ok(<${getSerDeTypeDeclaration(type, 'result')}>::default())\n`,
|
|
471
|
+
}
|
|
472
|
+
])}\n`;
|
|
473
|
+
content += `${indent.pop().get()}}\n`;
|
|
333
474
|
return content;
|
|
334
475
|
}
|
|
476
|
+
/**
|
|
477
|
+
* constructs a serde serialize function
|
|
478
|
+
*
|
|
479
|
+
* @param indent the indentation helper currently in scope
|
|
480
|
+
* @param type the type for which to build the helper
|
|
481
|
+
* @param use the use statement builder currently in scope
|
|
482
|
+
* @returns the pub fn serialize function definition
|
|
483
|
+
*/
|
|
484
|
+
function buildSerialize(indent, type, use) {
|
|
485
|
+
use.addTypes('serde', ['Serialize', 'Serializer']);
|
|
486
|
+
use.addType('std', 'result::Result');
|
|
487
|
+
use.addForType(type);
|
|
488
|
+
// clippy wants the outer-most Vec<T> to be a [] instead
|
|
489
|
+
const getTypeDeclaration = function (type) {
|
|
490
|
+
if (type.kind === 'Vec') {
|
|
491
|
+
return `[${helpers.getTypeDeclaration(type.type)}]`;
|
|
492
|
+
}
|
|
493
|
+
return helpers.getTypeDeclaration(type);
|
|
494
|
+
};
|
|
495
|
+
let content = `${indent.get()}pub fn serialize<S>(to_serialize: &${getTypeDeclaration(type)}, serializer: S) -> Result<S::Ok, S::Error>\n`;
|
|
496
|
+
content += `${indent.get()}where S: Serializer\n${indent.get()}{\n`;
|
|
497
|
+
content += recursiveBuildSerializeBody(indent.push(), use, {
|
|
498
|
+
caller: 'start',
|
|
499
|
+
type: type,
|
|
500
|
+
srcVar: 'to_serialize',
|
|
501
|
+
});
|
|
502
|
+
content += `${indent.pop().get()}}\n`;
|
|
503
|
+
return content;
|
|
504
|
+
}
|
|
505
|
+
// used by recursiveBuildDeserializeBody and recursiveBuildSerializeBody
|
|
506
|
+
// to determine recursion depth. used to uniquely name local variables.
|
|
507
|
+
let depth = 0;
|
|
508
|
+
/**
|
|
509
|
+
* recursive state machine to construct the body of the deserialize function.
|
|
510
|
+
*
|
|
511
|
+
* @param indent the indentation helper currently in scope
|
|
512
|
+
* @param use the use statement builder currently in scope
|
|
513
|
+
* @param ctx the current context of the state machine
|
|
514
|
+
* @returns the contents of the Some(to_deserialize) match arm
|
|
515
|
+
*/
|
|
516
|
+
function recursiveBuildDeserializeBody(indent, use, ctx) {
|
|
517
|
+
switch (ctx.caller) {
|
|
518
|
+
case 'start':
|
|
519
|
+
depth = 0;
|
|
520
|
+
break;
|
|
521
|
+
default:
|
|
522
|
+
depth += 1;
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
// the name of the destination var to hold the
|
|
526
|
+
// current decoded state. the name includes the
|
|
527
|
+
// recursion depth so each var name is unique.
|
|
528
|
+
const destVar = `decoded${depth}`;
|
|
529
|
+
/**
|
|
530
|
+
* adds the var in val to the collection, or does nothing
|
|
531
|
+
* depending on the value of caller.
|
|
532
|
+
*
|
|
533
|
+
* when valAsDefault is true, the value in val is returned.
|
|
534
|
+
* else the empty string is returned.
|
|
535
|
+
*/
|
|
536
|
+
const insertOrPush = function (val, valAsDefault) {
|
|
537
|
+
switch (ctx.caller) {
|
|
538
|
+
case 'hashmap':
|
|
539
|
+
return `${indent.get()}decoded${depth - 1}.insert(kv.0, ${val});\n`;
|
|
540
|
+
case 'vec':
|
|
541
|
+
return `${indent.get()}decoded${depth - 1}.push(${val});\n`;
|
|
542
|
+
default:
|
|
543
|
+
return valAsDefault ? val : '';
|
|
544
|
+
}
|
|
545
|
+
};
|
|
546
|
+
let content = '';
|
|
547
|
+
switch (ctx.type.kind) {
|
|
548
|
+
case 'encodedBytes': {
|
|
549
|
+
// terminal case (NEVER the start case)
|
|
550
|
+
use.addType('azure_core', 'base64');
|
|
551
|
+
const base64Decode = `base64::${ctx.type.encoding === 'std' ? 'decode' : 'decode_url_safe'}`;
|
|
552
|
+
content = `${base64Decode}(${ctx.srcVar}).map_err(serde::de::Error::custom)?`;
|
|
553
|
+
content = insertOrPush(content, true);
|
|
554
|
+
break;
|
|
555
|
+
}
|
|
556
|
+
case 'hashmap': {
|
|
557
|
+
content = `${indent.get()}let mut ${destVar} = <${getSerDeTypeDeclaration(ctx.type, 'result')}>::new();\n`;
|
|
558
|
+
content += `${indent.get()}for kv in ${ctx.srcVar} {\n`;
|
|
559
|
+
content += recursiveBuildDeserializeBody(indent.push(), use, {
|
|
560
|
+
caller: 'hashmap',
|
|
561
|
+
type: ctx.type.type,
|
|
562
|
+
srcVar: 'kv.1',
|
|
563
|
+
});
|
|
564
|
+
content += `${indent.pop().get()}}\n`; // end for
|
|
565
|
+
content += insertOrPush(destVar, false);
|
|
566
|
+
break;
|
|
567
|
+
}
|
|
568
|
+
case 'offsetDateTime': {
|
|
569
|
+
// terminal case (NEVER the start case)
|
|
570
|
+
if (ctx.type.encoding !== 'unix_time') {
|
|
571
|
+
use.addType('azure_core', 'date');
|
|
572
|
+
}
|
|
573
|
+
const dateParse = ctx.type.encoding === 'unix_time' ? 'OffsetDateTime::from_unix_timestamp' : `date::parse_${ctx.type.encoding}`;
|
|
574
|
+
content = `${dateParse}(${ctx.type.encoding !== 'unix_time' ? '&' : ''}${ctx.srcVar}).map_err(serde::de::Error::custom)?`;
|
|
575
|
+
content = insertOrPush(content, true);
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
case 'Vec': {
|
|
579
|
+
content = `${indent.get()}let mut ${destVar} = <${getSerDeTypeDeclaration(ctx.type, 'result')}>::new();\n`;
|
|
580
|
+
content += `${indent.get()}for v in ${ctx.srcVar} {\n`;
|
|
581
|
+
content += recursiveBuildDeserializeBody(indent.push(), use, {
|
|
582
|
+
caller: 'vec',
|
|
583
|
+
type: ctx.type.type,
|
|
584
|
+
srcVar: 'v',
|
|
585
|
+
});
|
|
586
|
+
content += `${indent.pop().get()}}\n`; // end for
|
|
587
|
+
content += insertOrPush(destVar, false);
|
|
588
|
+
break;
|
|
589
|
+
}
|
|
590
|
+
default:
|
|
591
|
+
throw new Error(`unexpected kind ${ctx.type.kind}`);
|
|
592
|
+
}
|
|
593
|
+
if (depth > 0) {
|
|
594
|
+
depth -= 1;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
content += `${indent.get()}Ok(${destVar})\n`;
|
|
598
|
+
}
|
|
599
|
+
return content;
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* recursive state machine to construct the body of the serialize function.
|
|
603
|
+
*
|
|
604
|
+
* @param indent the indentation helper currently in scope
|
|
605
|
+
* @param use the use statement builder currently in scope
|
|
606
|
+
* @param ctx the current context of the state machine
|
|
607
|
+
* @returns the contents of the serialize function
|
|
608
|
+
*/
|
|
609
|
+
function recursiveBuildSerializeBody(indent, use, ctx) {
|
|
610
|
+
switch (ctx.caller) {
|
|
611
|
+
case 'start':
|
|
612
|
+
depth = 0;
|
|
613
|
+
break;
|
|
614
|
+
default:
|
|
615
|
+
depth += 1;
|
|
616
|
+
break;
|
|
617
|
+
}
|
|
618
|
+
// the name of the destination var to hold the
|
|
619
|
+
// current encoded state. the name includes the
|
|
620
|
+
// recursion depth so each var name is unique.
|
|
621
|
+
const destVar = `encoded${depth}`;
|
|
622
|
+
/** inserts the var in val into the current HashMap<T, U> */
|
|
623
|
+
const hashMapInsert = function (val) {
|
|
624
|
+
return `${indent.get()}encoded${depth - 1}.insert(kv.0, ${val});\n`;
|
|
625
|
+
};
|
|
626
|
+
let content = '';
|
|
627
|
+
switch (ctx.type.kind) {
|
|
628
|
+
case 'encodedBytes': {
|
|
629
|
+
// terminal case (NEVER the start case)
|
|
630
|
+
use.addType('azure_core', 'base64');
|
|
631
|
+
const base64Encode = `base64::${ctx.type.encoding === 'std' ? 'encode' : 'encode_url_safe'}`;
|
|
632
|
+
switch (ctx.caller) {
|
|
633
|
+
case 'hashmap':
|
|
634
|
+
content = `${hashMapInsert(`${base64Encode}(${ctx.srcVar})`)}`;
|
|
635
|
+
break;
|
|
636
|
+
default:
|
|
637
|
+
content = base64Encode;
|
|
638
|
+
}
|
|
639
|
+
break;
|
|
640
|
+
}
|
|
641
|
+
case 'hashmap': {
|
|
642
|
+
let enumerateMap = `${indent.get()}let mut ${destVar} = <${getSerDeTypeDeclaration(ctx.type, 'serialize')}>::new();\n`;
|
|
643
|
+
enumerateMap += `${indent.get()}for kv in ${ctx.srcVar} {\n`;
|
|
644
|
+
enumerateMap += recursiveBuildSerializeBody(indent.push(), use, {
|
|
645
|
+
caller: 'hashmap',
|
|
646
|
+
type: ctx.type.type,
|
|
647
|
+
srcVar: 'kv.1',
|
|
648
|
+
});
|
|
649
|
+
enumerateMap += `${indent.pop().get()}}\n`; // end for
|
|
650
|
+
switch (ctx.caller) {
|
|
651
|
+
case 'hashmap':
|
|
652
|
+
content = enumerateMap;
|
|
653
|
+
content += `${hashMapInsert(destVar)}`;
|
|
654
|
+
break;
|
|
655
|
+
case 'start':
|
|
656
|
+
content = enumerateMap;
|
|
657
|
+
break;
|
|
658
|
+
case 'vec':
|
|
659
|
+
content = `|${ctx.srcVar}|{\n`;
|
|
660
|
+
content += enumerateMap;
|
|
661
|
+
content += `${indent.get()}encoded${depth}}`;
|
|
662
|
+
break;
|
|
663
|
+
}
|
|
664
|
+
break;
|
|
665
|
+
}
|
|
666
|
+
case 'offsetDateTime': {
|
|
667
|
+
// terminal case (NEVER the start case)
|
|
668
|
+
if (ctx.type.encoding !== 'unix_time') {
|
|
669
|
+
use.addType('azure_core', 'date');
|
|
670
|
+
}
|
|
671
|
+
content = ctx.type.encoding === 'unix_time' ? `${ctx.srcVar}.unix_timestamp()` : `date::to_${ctx.type.encoding}`;
|
|
672
|
+
switch (ctx.caller) {
|
|
673
|
+
case 'hashmap':
|
|
674
|
+
content = `${hashMapInsert(`${content}${ctx.type.encoding !== 'unix_time' ? `(${ctx.srcVar})` : ''}`)}`;
|
|
675
|
+
break;
|
|
676
|
+
case 'vec':
|
|
677
|
+
if (ctx.type.encoding === 'unix_time') {
|
|
678
|
+
content = `|v|${content}`;
|
|
679
|
+
}
|
|
680
|
+
break;
|
|
681
|
+
}
|
|
682
|
+
break;
|
|
683
|
+
}
|
|
684
|
+
case 'Vec': {
|
|
685
|
+
const convertVec = `.iter().map(${recursiveBuildSerializeBody(indent.push(), use, {
|
|
686
|
+
caller: 'vec',
|
|
687
|
+
type: ctx.type.type,
|
|
688
|
+
srcVar: 'v',
|
|
689
|
+
})}).collect()`;
|
|
690
|
+
indent.pop();
|
|
691
|
+
switch (ctx.caller) {
|
|
692
|
+
case 'hashmap':
|
|
693
|
+
content = `${hashMapInsert(`${ctx.srcVar}${convertVec}`)}`;
|
|
694
|
+
break;
|
|
695
|
+
case 'start':
|
|
696
|
+
content = `${indent.get()}let ${destVar} = ${ctx.srcVar}${convertVec};\n`;
|
|
697
|
+
break;
|
|
698
|
+
case 'vec':
|
|
699
|
+
content = `|${ctx.srcVar}|${ctx.srcVar}${convertVec}`;
|
|
700
|
+
break;
|
|
701
|
+
}
|
|
702
|
+
break;
|
|
703
|
+
}
|
|
704
|
+
default:
|
|
705
|
+
throw new Error(`unexpected kind ${ctx.type.kind}`);
|
|
706
|
+
}
|
|
707
|
+
if (depth > 0) {
|
|
708
|
+
depth -= 1;
|
|
709
|
+
}
|
|
710
|
+
else {
|
|
711
|
+
content += `${indent.get()}<${getSerDeTypeDeclaration(ctx.type, 'serialize')}>::serialize(&${destVar}, serializer)\n`;
|
|
712
|
+
}
|
|
713
|
+
return content;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* a specialization of helpers.getTypeDeclaration for constructing
|
|
717
|
+
* the target type declarations in the serde helpers.
|
|
718
|
+
* the type declarations are slightly different depending on the usage
|
|
719
|
+
* context and the underlying generic type.
|
|
720
|
+
*
|
|
721
|
+
* @param type is the Rust type for which to emit the declaration
|
|
722
|
+
* @param usage defines the context in which the type is being used.
|
|
723
|
+
* serialize - type is used in the serialize function
|
|
724
|
+
* deserialize - type is used in the deserialize function
|
|
725
|
+
* result - type is used as the result type in the deserialize function
|
|
726
|
+
* @returns
|
|
727
|
+
*/
|
|
728
|
+
function getSerDeTypeDeclaration(type, usage) {
|
|
729
|
+
switch (type.kind) {
|
|
730
|
+
case 'encodedBytes':
|
|
731
|
+
return usage === 'result' ? 'Vec<u8>' : 'String';
|
|
732
|
+
case 'offsetDateTime':
|
|
733
|
+
return usage === 'result' ? 'OffsetDateTime' : type.encoding === 'unix_time' ? 'i64' : 'String';
|
|
734
|
+
case 'hashmap':
|
|
735
|
+
return `${type.name}<${usage === 'serialize' ? '&' : ''}String, ${getSerDeTypeDeclaration(type.type, usage)}>`;
|
|
736
|
+
case 'Vec':
|
|
737
|
+
return `${type.kind}<${getSerDeTypeDeclaration(type.type, usage)}>`;
|
|
738
|
+
default:
|
|
739
|
+
throw new Error(`unexpected kind ${type.kind}`);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
335
742
|
//# sourceMappingURL=models.js.map
|