@azure-tools/typespec-rust 0.11.0 → 0.13.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.
Files changed (63) hide show
  1. package/dist/src/codegen/clients.d.ts +8 -13
  2. package/dist/src/codegen/clients.d.ts.map +1 -1
  3. package/dist/src/codegen/clients.js +45 -39
  4. package/dist/src/codegen/clients.js.map +1 -1
  5. package/dist/src/codegen/codeGenerator.d.ts +1 -2
  6. package/dist/src/codegen/codeGenerator.d.ts.map +1 -1
  7. package/dist/src/codegen/codeGenerator.js +28 -27
  8. package/dist/src/codegen/codeGenerator.js.map +1 -1
  9. package/dist/src/codegen/context.d.ts.map +1 -1
  10. package/dist/src/codegen/context.js +5 -4
  11. package/dist/src/codegen/context.js.map +1 -1
  12. package/dist/src/codegen/enums.d.ts +2 -1
  13. package/dist/src/codegen/enums.d.ts.map +1 -1
  14. package/dist/src/codegen/enums.js +6 -3
  15. package/dist/src/codegen/enums.js.map +1 -1
  16. package/dist/src/codegen/headerTraits.d.ts +2 -2
  17. package/dist/src/codegen/headerTraits.d.ts.map +1 -1
  18. package/dist/src/codegen/headerTraits.js +12 -9
  19. package/dist/src/codegen/headerTraits.js.map +1 -1
  20. package/dist/src/codegen/helpers.d.ts +11 -4
  21. package/dist/src/codegen/helpers.d.ts.map +1 -1
  22. package/dist/src/codegen/helpers.js +13 -8
  23. package/dist/src/codegen/helpers.js.map +1 -1
  24. package/dist/src/codegen/lib.d.ts +1 -4
  25. package/dist/src/codegen/lib.d.ts.map +1 -1
  26. package/dist/src/codegen/lib.js +4 -99
  27. package/dist/src/codegen/lib.js.map +1 -1
  28. package/dist/src/codegen/mod.d.ts +10 -4
  29. package/dist/src/codegen/mod.d.ts.map +1 -1
  30. package/dist/src/codegen/mod.js +36 -19
  31. package/dist/src/codegen/mod.js.map +1 -1
  32. package/dist/src/codegen/models.d.ts +5 -4
  33. package/dist/src/codegen/models.d.ts.map +1 -1
  34. package/dist/src/codegen/models.js +455 -48
  35. package/dist/src/codegen/models.js.map +1 -1
  36. package/dist/src/codegen/use.d.ts +7 -6
  37. package/dist/src/codegen/use.d.ts.map +1 -1
  38. package/dist/src/codegen/use.js +31 -16
  39. package/dist/src/codegen/use.js.map +1 -1
  40. package/dist/src/codemodel/client.d.ts +8 -3
  41. package/dist/src/codemodel/client.d.ts.map +1 -1
  42. package/dist/src/codemodel/client.js +9 -8
  43. package/dist/src/codemodel/client.js.map +1 -1
  44. package/dist/src/codemodel/method.d.ts +3 -3
  45. package/dist/src/codemodel/method.d.ts.map +1 -1
  46. package/dist/src/codemodel/method.js +2 -2
  47. package/dist/src/codemodel/method.js.map +1 -1
  48. package/dist/src/codemodel/types.d.ts +13 -11
  49. package/dist/src/codemodel/types.d.ts.map +1 -1
  50. package/dist/src/codemodel/types.js +20 -20
  51. package/dist/src/codemodel/types.js.map +1 -1
  52. package/dist/src/emitter.d.ts.map +1 -1
  53. package/dist/src/emitter.js +12 -6
  54. package/dist/src/emitter.js.map +1 -1
  55. package/dist/src/lib.d.ts +1 -0
  56. package/dist/src/lib.d.ts.map +1 -1
  57. package/dist/src/lib.js +2 -1
  58. package/dist/src/lib.js.map +1 -1
  59. package/dist/src/tcgcadapter/adapter.d.ts +3 -1
  60. package/dist/src/tcgcadapter/adapter.d.ts.map +1 -1
  61. package/dist/src/tcgcadapter/adapter.js +64 -50
  62. package/dist/src/tcgcadapter/adapter.js.map +1 -1
  63. 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, true),
22
+ public: emitModelsInternal(crate, context, 'pub'),
22
23
  serde: emitModelsSerde(crate, context),
23
- internal: emitModelsInternal(crate, context, false),
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 pub true if we want public models, false for internal models
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, pub) {
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' : undefined);
38
+ const use = new Use(visibility === 'pub' ? 'models' : 'modelsOther');
38
39
  use.addTypes('serde', ['Deserialize', 'Serialize']);
39
- use.addType('typespec_client_core::fmt', 'SafeDebug');
40
+ use.addType('azure_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,12 +53,12 @@ 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.internal === pub) {
56
+ if (model.visibility !== visibility) {
56
57
  continue;
57
58
  }
58
59
  const bodyFormat = context.getModelBodyFormat(model);
59
60
  body += helpers.formatDocComment(model.docs);
60
- body += helpers.annotationDerive('Default', 'azure_core::Model');
61
+ body += helpers.annotationDerive('Default', 'azure_core::http::Model');
61
62
  if ((model.flags & rust.ModelFlags.Output) === rust.ModelFlags.Output && (model.flags & rust.ModelFlags.Input) === 0) {
62
63
  // output-only models get the non_exhaustive annotation
63
64
  body += helpers.AnnotationNonExhaustive;
@@ -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 += `pub struct ${model.name} {\n`;
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.unwrapOption(field.type).kind === 'offsetDateTime') {
84
- serdeParams.add('default');
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 === 'vector' && field.xmlKind !== 'unwrappedList') {
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('crate', `generated::xml_helpers::${xmlListWrapper.name}`);
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 === 'vector') {
104
+ else if (field.type.kind === 'Vec') {
123
105
  serdeParams.add('default');
124
- // NOTE: we want to send an empty array for XML payloads
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.emitPub(field.pub)}${field.name}: ${helpers.getTypeDeclaration(field.type)},\n\n`;
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 (!pub) {
124
+ if (visibility !== 'pub') {
143
125
  for (const model of crate.models) {
144
- if (model.kind === 'marker' || !model.internal) {
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 content;
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.internal) {
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
- if (body.length === 0) {
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
- content += use.text();
186
- content += body;
187
- return content;
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