@azure-tools/typespec-go 0.1.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/LICENSE +21 -0
- package/README.md +16 -0
- package/dist/codegen.go/src/clientFactory.d.ts +3 -0
- package/dist/codegen.go/src/clientFactory.d.ts.map +1 -0
- package/dist/codegen.go/src/clientFactory.js +77 -0
- package/dist/codegen.go/src/clientFactory.js.map +1 -0
- package/dist/codegen.go/src/constants.d.ts +3 -0
- package/dist/codegen.go/src/constants.d.ts.map +1 -0
- package/dist/codegen.go/src/constants.js +59 -0
- package/dist/codegen.go/src/constants.js.map +1 -0
- package/dist/codegen.go/src/fake/factory.d.ts +3 -0
- package/dist/codegen.go/src/fake/factory.d.ts.map +1 -0
- package/dist/codegen.go/src/fake/factory.js +69 -0
- package/dist/codegen.go/src/fake/factory.js.map +1 -0
- package/dist/codegen.go/src/fake/internal.d.ts +14 -0
- package/dist/codegen.go/src/fake/internal.d.ts.map +1 -0
- package/dist/codegen.go/src/fake/internal.js +197 -0
- package/dist/codegen.go/src/fake/internal.js.map +1 -0
- package/dist/codegen.go/src/fake/servers.d.ts +14 -0
- package/dist/codegen.go/src/fake/servers.d.ts.map +1 -0
- package/dist/codegen.go/src/fake/servers.js +1255 -0
- package/dist/codegen.go/src/fake/servers.js.map +1 -0
- package/dist/codegen.go/src/gomod.d.ts +3 -0
- package/dist/codegen.go/src/gomod.d.ts.map +1 -0
- package/dist/codegen.go/src/gomod.js +55 -0
- package/dist/codegen.go/src/gomod.js.map +1 -0
- package/dist/codegen.go/src/helpers.d.ts +32 -0
- package/dist/codegen.go/src/helpers.d.ts.map +1 -0
- package/dist/codegen.go/src/helpers.js +586 -0
- package/dist/codegen.go/src/helpers.js.map +1 -0
- package/dist/codegen.go/src/imports.d.ts +11 -0
- package/dist/codegen.go/src/imports.d.ts.map +1 -0
- package/dist/codegen.go/src/imports.js +65 -0
- package/dist/codegen.go/src/imports.js.map +1 -0
- package/dist/codegen.go/src/interfaces.d.ts +3 -0
- package/dist/codegen.go/src/interfaces.d.ts.map +1 -0
- package/dist/codegen.go/src/interfaces.js +36 -0
- package/dist/codegen.go/src/interfaces.js.map +1 -0
- package/dist/codegen.go/src/models.d.ts +9 -0
- package/dist/codegen.go/src/models.d.ts.map +1 -0
- package/dist/codegen.go/src/models.js +849 -0
- package/dist/codegen.go/src/models.js.map +1 -0
- package/dist/codegen.go/src/operations.d.ts +9 -0
- package/dist/codegen.go/src/operations.d.ts.map +1 -0
- package/dist/codegen.go/src/operations.js +1337 -0
- package/dist/codegen.go/src/operations.js.map +1 -0
- package/dist/codegen.go/src/options.d.ts +3 -0
- package/dist/codegen.go/src/options.d.ts.map +1 -0
- package/dist/codegen.go/src/options.js +64 -0
- package/dist/codegen.go/src/options.js.map +1 -0
- package/dist/codegen.go/src/polymorphics.d.ts +3 -0
- package/dist/codegen.go/src/polymorphics.d.ts.map +1 -0
- package/dist/codegen.go/src/polymorphics.js +169 -0
- package/dist/codegen.go/src/polymorphics.js.map +1 -0
- package/dist/codegen.go/src/responses.d.ts +7 -0
- package/dist/codegen.go/src/responses.d.ts.map +1 -0
- package/dist/codegen.go/src/responses.js +167 -0
- package/dist/codegen.go/src/responses.js.map +1 -0
- package/dist/codegen.go/src/time.d.ts +8 -0
- package/dist/codegen.go/src/time.d.ts.map +1 -0
- package/dist/codegen.go/src/time.js +511 -0
- package/dist/codegen.go/src/time.js.map +1 -0
- package/dist/codemodel.go/src/client.d.ts +96 -0
- package/dist/codemodel.go/src/client.d.ts.map +1 -0
- package/dist/codemodel.go/src/client.js +114 -0
- package/dist/codemodel.go/src/client.js.map +1 -0
- package/dist/codemodel.go/src/index.d.ts +6 -0
- package/dist/codemodel.go/src/index.d.ts.map +1 -0
- package/dist/codemodel.go/src/index.js +10 -0
- package/dist/codemodel.go/src/index.js.map +1 -0
- package/dist/codemodel.go/src/package.d.ts +49 -0
- package/dist/codemodel.go/src/package.d.ts.map +1 -0
- package/dist/codemodel.go/src/package.js +86 -0
- package/dist/codemodel.go/src/package.js.map +1 -0
- package/dist/codemodel.go/src/param.d.ts +162 -0
- package/dist/codemodel.go/src/param.d.ts.map +1 -0
- package/dist/codemodel.go/src/param.js +189 -0
- package/dist/codemodel.go/src/param.js.map +1 -0
- package/dist/codemodel.go/src/result.d.ts +102 -0
- package/dist/codemodel.go/src/result.d.ts.map +1 -0
- package/dist/codemodel.go/src/result.js +119 -0
- package/dist/codemodel.go/src/result.js.map +1 -0
- package/dist/codemodel.go/src/type.d.ts +181 -0
- package/dist/codemodel.go/src/type.d.ts.map +1 -0
- package/dist/codemodel.go/src/type.js +242 -0
- package/dist/codemodel.go/src/type.js.map +1 -0
- package/dist/naming.go/src/mappings.d.ts +3 -0
- package/dist/naming.go/src/mappings.d.ts.map +1 -0
- package/dist/naming.go/src/mappings.js +128 -0
- package/dist/naming.go/src/mappings.js.map +1 -0
- package/dist/naming.go/src/naming.d.ts +10 -0
- package/dist/naming.go/src/naming.d.ts.map +1 -0
- package/dist/naming.go/src/naming.js +114 -0
- package/dist/naming.go/src/naming.js.map +1 -0
- package/dist/typespec-go/src/emitter.d.ts +5 -0
- package/dist/typespec-go/src/emitter.d.ts.map +1 -0
- package/dist/typespec-go/src/emitter.js +122 -0
- package/dist/typespec-go/src/emitter.js.map +1 -0
- package/dist/typespec-go/src/index.d.ts +3 -0
- package/dist/typespec-go/src/index.d.ts.map +1 -0
- package/dist/typespec-go/src/index.js +7 -0
- package/dist/typespec-go/src/index.js.map +1 -0
- package/dist/typespec-go/src/lib.d.ts +25 -0
- package/dist/typespec-go/src/lib.d.ts.map +1 -0
- package/dist/typespec-go/src/lib.js +36 -0
- package/dist/typespec-go/src/lib.js.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.d.ts +5 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.d.ts.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.js +119 -0
- package/dist/typespec-go/src/tcgcadapter/adapter.js.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/clients.d.ts +26 -0
- package/dist/typespec-go/src/tcgcadapter/clients.d.ts.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/clients.js +621 -0
- package/dist/typespec-go/src/tcgcadapter/clients.js.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/types.d.ts +29 -0
- package/dist/typespec-go/src/tcgcadapter/types.d.ts.map +1 -0
- package/dist/typespec-go/src/tcgcadapter/types.js +975 -0
- package/dist/typespec-go/src/tcgcadapter/types.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,849 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
+
* Licensed under the MIT License. See License.txt in the project root for license information.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
import * as go from '../../codemodel.go/src/index.js';
|
|
6
|
+
import { capitalize, comment } from '@azure-tools/codegen';
|
|
7
|
+
import { values } from '@azure-tools/linq';
|
|
8
|
+
import { commentLength, contentPreamble, formatLiteralValue, recursiveUnwrapMapSlice, sortAscending } from './helpers.js';
|
|
9
|
+
import { ImportManager } from './imports.js';
|
|
10
|
+
// Creates the content in models.go
|
|
11
|
+
export async function generateModels(codeModel) {
|
|
12
|
+
if (codeModel.models.length === 0) {
|
|
13
|
+
return {
|
|
14
|
+
models: '',
|
|
15
|
+
serDe: ''
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
// this list of packages to import
|
|
19
|
+
const modelImports = new ImportManager();
|
|
20
|
+
const serdeImports = new ImportManager();
|
|
21
|
+
let modelText = contentPreamble(codeModel);
|
|
22
|
+
// we do model generation first as it can add imports to the imports list
|
|
23
|
+
const modelDefs = generateModelDefs(modelImports, serdeImports, codeModel);
|
|
24
|
+
modelText += modelImports.text();
|
|
25
|
+
// structs
|
|
26
|
+
let needsJSONPopulate = false;
|
|
27
|
+
let needsJSONUnpopulate = false;
|
|
28
|
+
let needsJSONPopulateByteArray = false;
|
|
29
|
+
let needsJSONPopulateAny = false;
|
|
30
|
+
let needsJSONPopulateMultipart = false;
|
|
31
|
+
let serdeTextBody = '';
|
|
32
|
+
for (const modelDef of values(modelDefs)) {
|
|
33
|
+
modelText += modelDef.text();
|
|
34
|
+
modelDef.Methods.sort((a, b) => { return sortAscending(a.name, b.name); });
|
|
35
|
+
for (const method of values(modelDef.Methods)) {
|
|
36
|
+
if (method.desc.length > 0) {
|
|
37
|
+
modelText += `${comment(method.desc, '// ', undefined, commentLength)}\n`;
|
|
38
|
+
}
|
|
39
|
+
modelText += method.text;
|
|
40
|
+
}
|
|
41
|
+
modelDef.SerDe.methods.sort((a, b) => { return sortAscending(a.name, b.name); });
|
|
42
|
+
for (const method of values(modelDef.SerDe.methods)) {
|
|
43
|
+
if (method.desc.length > 0) {
|
|
44
|
+
serdeTextBody += `${comment(method.desc, '// ', undefined, commentLength)}\n`;
|
|
45
|
+
}
|
|
46
|
+
serdeTextBody += method.text;
|
|
47
|
+
}
|
|
48
|
+
if (modelDef.SerDe.needsJSONPopulate) {
|
|
49
|
+
needsJSONPopulate = true;
|
|
50
|
+
}
|
|
51
|
+
if (modelDef.SerDe.needsJSONUnpopulate) {
|
|
52
|
+
needsJSONUnpopulate = true;
|
|
53
|
+
}
|
|
54
|
+
if (modelDef.SerDe.needsJSONPopulateByteArray) {
|
|
55
|
+
needsJSONPopulateByteArray = true;
|
|
56
|
+
}
|
|
57
|
+
if (modelDef.SerDe.needsJSONPopulateAny) {
|
|
58
|
+
needsJSONPopulateAny = true;
|
|
59
|
+
}
|
|
60
|
+
if (modelDef.SerDe.needsJSONPopulateMultipart) {
|
|
61
|
+
needsJSONPopulateMultipart = true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (needsJSONPopulate) {
|
|
65
|
+
serdeImports.add('reflect');
|
|
66
|
+
serdeImports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore');
|
|
67
|
+
serdeTextBody += 'func populate(m map[string]any, k string, v any) {\n';
|
|
68
|
+
serdeTextBody += '\tif v == nil {\n';
|
|
69
|
+
serdeTextBody += '\t\treturn\n';
|
|
70
|
+
serdeTextBody += '\t} else if azcore.IsNullValue(v) {\n';
|
|
71
|
+
serdeTextBody += '\t\tm[k] = nil\n';
|
|
72
|
+
serdeTextBody += '\t} else if !reflect.ValueOf(v).IsNil() {\n';
|
|
73
|
+
serdeTextBody += '\t\tm[k] = v\n';
|
|
74
|
+
serdeTextBody += '\t}\n';
|
|
75
|
+
serdeTextBody += '}\n\n';
|
|
76
|
+
}
|
|
77
|
+
if (needsJSONPopulateAny) {
|
|
78
|
+
serdeImports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore');
|
|
79
|
+
serdeTextBody += 'func populateAny(m map[string]any, k string, v any) {\n';
|
|
80
|
+
serdeTextBody += '\tif v == nil {\n';
|
|
81
|
+
serdeTextBody += '\t\treturn\n';
|
|
82
|
+
serdeTextBody += '\t} else if azcore.IsNullValue(v) {\n';
|
|
83
|
+
serdeTextBody += '\t\tm[k] = nil\n';
|
|
84
|
+
serdeTextBody += '\t} else {\n';
|
|
85
|
+
serdeTextBody += '\t\tm[k] = v\n';
|
|
86
|
+
serdeTextBody += '\t}\n';
|
|
87
|
+
serdeTextBody += '}\n\n';
|
|
88
|
+
}
|
|
89
|
+
if (needsJSONPopulateByteArray) {
|
|
90
|
+
serdeImports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore');
|
|
91
|
+
serdeImports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
92
|
+
serdeTextBody += 'func populateByteArray[T any](m map[string]any, k string, b []T, convert func() any) {\n';
|
|
93
|
+
serdeTextBody += '\tif azcore.IsNullValue(b) {\n';
|
|
94
|
+
serdeTextBody += '\t\tm[k] = nil\n';
|
|
95
|
+
serdeTextBody += '\t} else if len(b) == 0 {\n';
|
|
96
|
+
serdeTextBody += '\t\treturn\n';
|
|
97
|
+
serdeTextBody += '\t} else {\n';
|
|
98
|
+
serdeTextBody += '\t\tm[k] = convert()\n';
|
|
99
|
+
serdeTextBody += '\t}\n';
|
|
100
|
+
serdeTextBody += '}\n\n';
|
|
101
|
+
}
|
|
102
|
+
if (needsJSONUnpopulate) {
|
|
103
|
+
serdeImports.add('fmt');
|
|
104
|
+
serdeTextBody += 'func unpopulate(data json.RawMessage, fn string, v any) error {\n';
|
|
105
|
+
serdeTextBody += '\tif data == nil || string(data) == "null" {\n';
|
|
106
|
+
serdeTextBody += '\t\treturn nil\n';
|
|
107
|
+
serdeTextBody += '\t}\n';
|
|
108
|
+
serdeTextBody += '\tif err := json.Unmarshal(data, v); err != nil {\n';
|
|
109
|
+
serdeTextBody += '\t\treturn fmt.Errorf("struct field %s: %v", fn, err)\n';
|
|
110
|
+
serdeTextBody += '\t}\n';
|
|
111
|
+
serdeTextBody += '\treturn nil\n';
|
|
112
|
+
serdeTextBody += '}\n\n';
|
|
113
|
+
}
|
|
114
|
+
if (needsJSONPopulateMultipart) {
|
|
115
|
+
serdeImports.add('encoding/json');
|
|
116
|
+
serdeTextBody += 'func populateMultipartJSON(m map[string]any, k string, v any) error {\n';
|
|
117
|
+
serdeTextBody += '\tdata, err := json.Marshal(v)\n';
|
|
118
|
+
serdeTextBody += '\tif err != nil {\n\t\treturn err\n\t}\n';
|
|
119
|
+
serdeTextBody += '\tm[k] = data\n\treturn nil\n';
|
|
120
|
+
serdeTextBody += '}\n\n';
|
|
121
|
+
}
|
|
122
|
+
let serdeText = '';
|
|
123
|
+
if (serdeTextBody.length > 0) {
|
|
124
|
+
serdeText = contentPreamble(codeModel);
|
|
125
|
+
serdeText += serdeImports.text();
|
|
126
|
+
serdeText += serdeTextBody;
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
models: modelText,
|
|
130
|
+
serDe: serdeText
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function generateModelDefs(modelImports, serdeImports, codeModel) {
|
|
134
|
+
var _a;
|
|
135
|
+
const models = codeModel.models;
|
|
136
|
+
const modelDefs = new Array();
|
|
137
|
+
for (const model of models) {
|
|
138
|
+
for (const field of model.fields) {
|
|
139
|
+
const descriptionMods = new Array();
|
|
140
|
+
if (field.annotations.readOnly) {
|
|
141
|
+
descriptionMods.push('READ-ONLY');
|
|
142
|
+
}
|
|
143
|
+
else if (field.annotations.required && (!go.isLiteralValue(field.type) || model.usage === go.UsageFlags.Output)) {
|
|
144
|
+
descriptionMods.push('REQUIRED');
|
|
145
|
+
}
|
|
146
|
+
else if (go.isLiteralValue(field.type)) {
|
|
147
|
+
if (!field.annotations.required) {
|
|
148
|
+
descriptionMods.push('FLAG');
|
|
149
|
+
}
|
|
150
|
+
descriptionMods.push('CONSTANT');
|
|
151
|
+
}
|
|
152
|
+
if (go.isLiteralValue(field.type) && model.usage !== go.UsageFlags.Output) {
|
|
153
|
+
// add a comment with the const value for const properties that are sent over the wire
|
|
154
|
+
if (field.description) {
|
|
155
|
+
field.description += '\n';
|
|
156
|
+
}
|
|
157
|
+
field.description += `Field has constant value ${formatLiteralValue(field.type, false)}, any specified value is ignored.`;
|
|
158
|
+
}
|
|
159
|
+
if (field.description) {
|
|
160
|
+
descriptionMods.push(field.description);
|
|
161
|
+
}
|
|
162
|
+
else if (go.isSliceType(field.type) && field.type.rawJSONAsBytes) {
|
|
163
|
+
// add a basic description if one isn't available
|
|
164
|
+
descriptionMods.push('The contents of this field are raw JSON.');
|
|
165
|
+
}
|
|
166
|
+
field.description = descriptionMods.join('; ');
|
|
167
|
+
}
|
|
168
|
+
const modelDef = new ModelDef(model.name, model.format, model.fields, model.description);
|
|
169
|
+
for (const field of values(modelDef.Fields)) {
|
|
170
|
+
modelImports.addImportForType(field.type);
|
|
171
|
+
}
|
|
172
|
+
if (go.isModelType(model) && model.format === 'xml' && !model.annotations.omitSerDeMethods) {
|
|
173
|
+
serdeImports.add('encoding/xml');
|
|
174
|
+
let needsDateTimeMarshalling = false;
|
|
175
|
+
let byteArrayFormat = false;
|
|
176
|
+
for (const field of values(model.fields)) {
|
|
177
|
+
serdeImports.addImportForType(field.type);
|
|
178
|
+
if (go.isTimeType(field.type)) {
|
|
179
|
+
needsDateTimeMarshalling = true;
|
|
180
|
+
}
|
|
181
|
+
else if (go.isBytesType(field.type)) {
|
|
182
|
+
byteArrayFormat = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// due to differences in XML marshallers/unmarshallers, we use different codegen than for JSON
|
|
186
|
+
if (needsDateTimeMarshalling || ((_a = model.xml) === null || _a === void 0 ? void 0 : _a.wrapper) || needsXMLArrayMarshalling(model) || byteArrayFormat) {
|
|
187
|
+
generateXMLMarshaller(model, modelDef, serdeImports);
|
|
188
|
+
if (needsDateTimeMarshalling || byteArrayFormat) {
|
|
189
|
+
generateXMLUnmarshaller(model, modelDef, serdeImports);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else if (needsXMLDictionaryUnmarshalling(model)) {
|
|
193
|
+
generateXMLUnmarshaller(model, modelDef, serdeImports);
|
|
194
|
+
}
|
|
195
|
+
modelDefs.push(modelDef);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
if (go.isPolymorphicType(model)) {
|
|
199
|
+
generateDiscriminatorMarkerMethod(model.interface, modelDef);
|
|
200
|
+
for (let parent = model.interface.parent; parent !== undefined; parent = parent.parent) {
|
|
201
|
+
generateDiscriminatorMarkerMethod(parent, modelDef);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (model.annotations.multipartFormData) {
|
|
205
|
+
generateToMultipartForm(modelDef);
|
|
206
|
+
modelDef.SerDe.needsJSONPopulateMultipart = true;
|
|
207
|
+
}
|
|
208
|
+
else if (!model.annotations.omitSerDeMethods) {
|
|
209
|
+
generateJSONMarshaller(model, modelDef, serdeImports);
|
|
210
|
+
generateJSONUnmarshaller(model, modelDef, serdeImports, codeModel.options);
|
|
211
|
+
}
|
|
212
|
+
modelDefs.push(modelDef);
|
|
213
|
+
}
|
|
214
|
+
return modelDefs;
|
|
215
|
+
}
|
|
216
|
+
function needsXMLDictionaryUnmarshalling(modelType) {
|
|
217
|
+
for (const field of values(modelType.fields)) {
|
|
218
|
+
// additional properties uses an internal wrapper type with its own unmarshaller
|
|
219
|
+
if (go.isMapType(field.type) && !field.annotations.isAdditionalProperties) {
|
|
220
|
+
return true;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
function needsXMLArrayMarshalling(modelType) {
|
|
226
|
+
for (const prop of values(modelType.fields)) {
|
|
227
|
+
if (go.isSliceType(prop.type)) {
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
// generates discriminator marker method
|
|
234
|
+
function generateDiscriminatorMarkerMethod(type, modelDef) {
|
|
235
|
+
const typeName = type.rootType.name;
|
|
236
|
+
const receiver = modelDef.receiverName();
|
|
237
|
+
const interfaceMethod = `Get${typeName}`;
|
|
238
|
+
let method = `func (${receiver} *${modelDef.Name}) ${interfaceMethod}() *${typeName} {`;
|
|
239
|
+
if (type.rootType.name === modelDef.Name) {
|
|
240
|
+
// the marker method is on the discriminator itself, so just return the receiver
|
|
241
|
+
method += ` return ${receiver} }\n\n`;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
// the marker method is on a child type, so return an instance of the parent
|
|
245
|
+
// type by copying the parent values into a new instance.
|
|
246
|
+
method += `\n\treturn &${type.rootType.name}{\n`;
|
|
247
|
+
for (const field of values(type.rootType.fields)) {
|
|
248
|
+
method += `\t\t${field.name}: ${modelDef.receiverName()}.${field.name},\n`;
|
|
249
|
+
}
|
|
250
|
+
method += '\t}\n}\n\n';
|
|
251
|
+
}
|
|
252
|
+
modelDef.Methods.push({ name: interfaceMethod, desc: `${interfaceMethod} implements the ${type.name} interface for type ${modelDef.Name}.`, text: method });
|
|
253
|
+
}
|
|
254
|
+
function generateToMultipartForm(modelDef) {
|
|
255
|
+
const receiver = modelDef.receiverName();
|
|
256
|
+
let method = `func (${receiver} ${modelDef.Name}) toMultipartFormData() (map[string]any, error) {\n`;
|
|
257
|
+
method += '\tobjectMap := make(map[string]any)\n';
|
|
258
|
+
for (const field of modelDef.Fields) {
|
|
259
|
+
const fieldType = recursiveUnwrapMapSlice(field.type);
|
|
260
|
+
let setField;
|
|
261
|
+
let star = '';
|
|
262
|
+
if (!field.byValue) {
|
|
263
|
+
star = '*';
|
|
264
|
+
}
|
|
265
|
+
if (go.isModelType(fieldType) && !fieldType.annotations.multipartFormData) {
|
|
266
|
+
setField = `\tif err := populateMultipartJSON(objectMap, "${field.serializedName}", ${star}${receiver}.${field.name}); err != nil {\n\t\treturn nil, err\n\t}\n`;
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
setField = `\tobjectMap["${field.serializedName}"] = ${star}${receiver}.${field.name}\n`;
|
|
270
|
+
}
|
|
271
|
+
if (!field.byValue) {
|
|
272
|
+
setField = `\tif ${receiver}.${field.name} != nil {\n\t\t${setField}\t}\n`;
|
|
273
|
+
}
|
|
274
|
+
method += setField;
|
|
275
|
+
}
|
|
276
|
+
method += '\treturn objectMap, nil\n}\n\n';
|
|
277
|
+
modelDef.SerDe.methods.push({ name: 'toMultipartFormData', desc: `toMultipartFormData converts ${modelDef.Name} to multipart/form data.`, text: method });
|
|
278
|
+
}
|
|
279
|
+
function generateJSONMarshaller(modelType, modelDef, imports) {
|
|
280
|
+
if (go.isModelType(modelType) && modelType.fields.length === 0) {
|
|
281
|
+
// non-discriminated types without content don't need a custom marshaller.
|
|
282
|
+
// there is a case in network where child is allOf base and child has no properties.
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
imports.add('encoding/json');
|
|
286
|
+
const typeName = modelDef.Name;
|
|
287
|
+
const receiver = modelDef.receiverName();
|
|
288
|
+
let marshaller = `func (${receiver} ${typeName}) MarshalJSON() ([]byte, error) {\n`;
|
|
289
|
+
marshaller += '\tobjectMap := make(map[string]any)\n';
|
|
290
|
+
marshaller += generateJSONMarshallerBody(modelType, modelDef, receiver, imports);
|
|
291
|
+
marshaller += '\treturn json.Marshal(objectMap)\n';
|
|
292
|
+
marshaller += '}\n\n';
|
|
293
|
+
modelDef.SerDe.methods.push({ name: 'MarshalJSON', desc: `MarshalJSON implements the json.Marshaller interface for type ${typeName}.`, text: marshaller });
|
|
294
|
+
}
|
|
295
|
+
function generateJSONMarshallerBody(modelType, modelDef, receiver, imports) {
|
|
296
|
+
let marshaller = '';
|
|
297
|
+
let addlProps;
|
|
298
|
+
for (const field of values(modelType.fields)) {
|
|
299
|
+
if (go.isMapType(field.type) && field.annotations.isAdditionalProperties) {
|
|
300
|
+
addlProps = field.type;
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
if (field.annotations.isDiscriminator) {
|
|
304
|
+
if (field.defaultValue) {
|
|
305
|
+
marshaller += `\tobjectMap["${field.serializedName}"] = ${formatLiteralValue(field.defaultValue, true)}\n`;
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
// if there's no discriminator value (e.g. Fish in test server), use the field's value.
|
|
309
|
+
// this will enable support for custom types that aren't (yet) described in the swagger.
|
|
310
|
+
marshaller += `\tobjectMap["${field.serializedName}"] = ${receiver}.${field.name}\n`;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
else if (go.isBytesType(field.type)) {
|
|
314
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
315
|
+
marshaller += `\tpopulateByteArray(objectMap, "${field.serializedName}", ${receiver}.${field.name}, func() any {\n`;
|
|
316
|
+
marshaller += `\t\treturn runtime.EncodeByteArray(${receiver}.${field.name}, runtime.Base64${field.type.encoding}Format)\n\t})\n`;
|
|
317
|
+
modelDef.SerDe.needsJSONPopulateByteArray = true;
|
|
318
|
+
}
|
|
319
|
+
else if (go.isSliceType(field.type) && go.isBytesType(field.type.elementType)) {
|
|
320
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
321
|
+
marshaller += `\tpopulateByteArray(objectMap, "${field.serializedName}", ${receiver}.${field.name}, func() any {\n`;
|
|
322
|
+
marshaller += `\t\tencodedValue := make([]string, len(${receiver}.${field.name}))\n`;
|
|
323
|
+
marshaller += `\t\tfor i := 0; i < len(${receiver}.${field.name}); i++ {\n`;
|
|
324
|
+
marshaller += `\t\t\tencodedValue[i] = runtime.EncodeByteArray(${receiver}.${field.name}[i], runtime.Base64${field.type.elementType.encoding}Format)\n\t\t}\n`;
|
|
325
|
+
marshaller += '\t\treturn encodedValue\n\t})\n';
|
|
326
|
+
modelDef.SerDe.needsJSONPopulateByteArray = true;
|
|
327
|
+
}
|
|
328
|
+
else if (go.isSliceType(field.type) && go.isTimeType(field.type.elementType)) {
|
|
329
|
+
const source = `${receiver}.${field.name}`;
|
|
330
|
+
let elementPtr = '*';
|
|
331
|
+
if (field.type.elementTypeByValue) {
|
|
332
|
+
elementPtr = '';
|
|
333
|
+
}
|
|
334
|
+
marshaller += `\taux := make([]${elementPtr}${field.type.elementType.dateTimeFormat}, len(${source}), len(${source}))\n`;
|
|
335
|
+
marshaller += `\tfor i := 0; i < len(${source}); i++ {\n`;
|
|
336
|
+
marshaller += `\t\taux[i] = (${elementPtr}${field.type.elementType.dateTimeFormat})(${source}[i])\n`;
|
|
337
|
+
marshaller += '\t}\n';
|
|
338
|
+
marshaller += `\tpopulate(objectMap, "${field.serializedName}", aux)\n`;
|
|
339
|
+
modelDef.SerDe.needsJSONPopulate = true;
|
|
340
|
+
}
|
|
341
|
+
else if (go.isLiteralValue(field.type)) {
|
|
342
|
+
const setter = `objectMap["${field.serializedName}"] = ${formatLiteralValue(field.type, true)}`;
|
|
343
|
+
if (!field.annotations.required) {
|
|
344
|
+
marshaller += `\tif ${receiver}.${field.name} != nil {\n\t\t${setter}\n\t}\n`;
|
|
345
|
+
}
|
|
346
|
+
else {
|
|
347
|
+
marshaller += `\t${setter}\n`;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
else if (go.isSliceType(field.type) && field.type.rawJSONAsBytes) {
|
|
351
|
+
marshaller += `\tpopulate(objectMap, "${field.serializedName}", json.RawMessage(${receiver}.${field.name}))\n`;
|
|
352
|
+
modelDef.SerDe.needsJSONPopulate = true;
|
|
353
|
+
}
|
|
354
|
+
else {
|
|
355
|
+
if (field.defaultValue) {
|
|
356
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/to');
|
|
357
|
+
marshaller += `\tif ${receiver}.${field.name} == nil {\n\t\t${receiver}.${field.name} = to.Ptr(${formatLiteralValue(field.defaultValue, true)})\n\t}\n`;
|
|
358
|
+
}
|
|
359
|
+
let populate = 'populate';
|
|
360
|
+
if (go.isTimeType(field.type)) {
|
|
361
|
+
populate += capitalize(field.type.dateTimeFormat);
|
|
362
|
+
modelDef.SerDe.needsJSONPopulate = true;
|
|
363
|
+
}
|
|
364
|
+
else if (go.isPrimitiveType(field.type) && field.type.typeName === 'any') {
|
|
365
|
+
populate += 'Any';
|
|
366
|
+
modelDef.SerDe.needsJSONPopulateAny = true;
|
|
367
|
+
}
|
|
368
|
+
else {
|
|
369
|
+
modelDef.SerDe.needsJSONPopulate = true;
|
|
370
|
+
}
|
|
371
|
+
marshaller += `\t${populate}(objectMap, "${field.serializedName}", ${receiver}.${field.name})\n`;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
if (addlProps) {
|
|
375
|
+
marshaller += `\tif ${receiver}.AdditionalProperties != nil {\n`;
|
|
376
|
+
marshaller += `\t\tfor key, val := range ${receiver}.AdditionalProperties {\n`;
|
|
377
|
+
let assignment = 'val';
|
|
378
|
+
if (go.isTimeType(addlProps.valueType)) {
|
|
379
|
+
assignment = `(*${addlProps.valueType.dateTimeFormat})(val)`;
|
|
380
|
+
}
|
|
381
|
+
marshaller += `\t\t\tobjectMap[key] = ${assignment}\n`;
|
|
382
|
+
marshaller += '\t\t}\n';
|
|
383
|
+
marshaller += '\t}\n';
|
|
384
|
+
}
|
|
385
|
+
return marshaller;
|
|
386
|
+
}
|
|
387
|
+
function generateJSONUnmarshaller(modelType, modelDef, imports, options) {
|
|
388
|
+
// there's a corner-case where a derived type might not add any new fields (Cookiecuttershark).
|
|
389
|
+
// in this case skip adding the unmarshaller as it's not necessary and doesn't compile.
|
|
390
|
+
if (modelDef.Fields.length === 0) {
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
imports.add('encoding/json');
|
|
394
|
+
imports.add('fmt');
|
|
395
|
+
const typeName = modelDef.Name;
|
|
396
|
+
const receiver = modelDef.receiverName();
|
|
397
|
+
let unmarshaller = `func (${receiver} *${typeName}) UnmarshalJSON(data []byte) error {\n`;
|
|
398
|
+
unmarshaller += '\tvar rawMsg map[string]json.RawMessage\n';
|
|
399
|
+
unmarshaller += '\tif err := json.Unmarshal(data, &rawMsg); err != nil {\n';
|
|
400
|
+
unmarshaller += `\t\treturn fmt.Errorf("unmarshalling type %T: %v", ${receiver}, err)\n`;
|
|
401
|
+
unmarshaller += '\t}\n';
|
|
402
|
+
unmarshaller += generateJSONUnmarshallerBody(modelType, modelDef, receiver, imports, options);
|
|
403
|
+
unmarshaller += '}\n\n';
|
|
404
|
+
modelDef.SerDe.methods.push({ name: 'UnmarshalJSON', desc: `UnmarshalJSON implements the json.Unmarshaller interface for type ${typeName}.`, text: unmarshaller });
|
|
405
|
+
}
|
|
406
|
+
function generateJSONUnmarshallerBody(modelType, modelDef, receiver, imports, options) {
|
|
407
|
+
const emitAddlProps = function (tab, addlProps) {
|
|
408
|
+
let addlPropsText = `${tab}\t\tif ${receiver}.AdditionalProperties == nil {\n`;
|
|
409
|
+
let ref = '';
|
|
410
|
+
if (!addlProps.valueTypeByValue) {
|
|
411
|
+
ref = '&';
|
|
412
|
+
}
|
|
413
|
+
addlPropsText += `${tab}\t\t\t${receiver}.AdditionalProperties = ${go.getTypeDeclaration(addlProps)}{}\n`;
|
|
414
|
+
addlPropsText += `${tab}\t\t}\n`;
|
|
415
|
+
addlPropsText += `${tab}\t\tif val != nil {\n`;
|
|
416
|
+
let auxType = go.getTypeDeclaration(addlProps.valueType);
|
|
417
|
+
let assignment = `${ref}aux`;
|
|
418
|
+
if (go.isTimeType(addlProps.valueType)) {
|
|
419
|
+
imports.add('time');
|
|
420
|
+
auxType = addlProps.valueType.dateTimeFormat;
|
|
421
|
+
assignment = `(*time.Time)(${assignment})`;
|
|
422
|
+
}
|
|
423
|
+
addlPropsText += `${tab}\t\t\tvar aux ${auxType}\n`;
|
|
424
|
+
addlPropsText += `${tab}\t\t\terr = json.Unmarshal(val, &aux)\n`;
|
|
425
|
+
addlPropsText += `${tab}\t\t\t${receiver}.AdditionalProperties[key] = ${assignment}\n`;
|
|
426
|
+
addlPropsText += `${tab}\t\t}\n`;
|
|
427
|
+
addlPropsText += `${tab}\t\tdelete(rawMsg, key)\n`;
|
|
428
|
+
return addlPropsText;
|
|
429
|
+
};
|
|
430
|
+
let unmarshalBody = '';
|
|
431
|
+
unmarshalBody = '\tfor key, val := range rawMsg {\n';
|
|
432
|
+
unmarshalBody += '\t\tvar err error\n';
|
|
433
|
+
unmarshalBody += '\t\tswitch key {\n';
|
|
434
|
+
let addlProps;
|
|
435
|
+
for (const field of values(modelType.fields)) {
|
|
436
|
+
if (go.isMapType(field.type) && field.annotations.isAdditionalProperties) {
|
|
437
|
+
addlProps = field.type;
|
|
438
|
+
continue;
|
|
439
|
+
}
|
|
440
|
+
unmarshalBody += `\t\tcase "${field.serializedName}":\n`;
|
|
441
|
+
if (hasDiscriminatorInterface(field.type)) {
|
|
442
|
+
unmarshalBody += generateDiscriminatorUnmarshaller(field, receiver);
|
|
443
|
+
}
|
|
444
|
+
else if (go.isTimeType(field.type)) {
|
|
445
|
+
unmarshalBody += `\t\t\t\terr = unpopulate${capitalize(field.type.dateTimeFormat)}(val, "${field.name}", &${receiver}.${field.name})\n`;
|
|
446
|
+
modelDef.SerDe.needsJSONUnpopulate = true;
|
|
447
|
+
}
|
|
448
|
+
else if (go.isSliceType(field.type) && go.isTimeType(field.type.elementType)) {
|
|
449
|
+
imports.add('time');
|
|
450
|
+
let elementPtr = '*';
|
|
451
|
+
if (field.type.elementTypeByValue) {
|
|
452
|
+
elementPtr = '';
|
|
453
|
+
}
|
|
454
|
+
unmarshalBody += `\t\t\tvar aux []${elementPtr}${field.type.elementType.dateTimeFormat}\n`;
|
|
455
|
+
unmarshalBody += `\t\t\terr = unpopulate(val, "${field.name}", &aux)\n`;
|
|
456
|
+
unmarshalBody += '\t\t\tfor _, au := range aux {\n';
|
|
457
|
+
unmarshalBody += `\t\t\t\t${receiver}.${field.name} = append(${receiver}.${field.name}, (${elementPtr}time.Time)(au))\n`;
|
|
458
|
+
unmarshalBody += '\t\t\t}\n';
|
|
459
|
+
modelDef.SerDe.needsJSONUnpopulate = true;
|
|
460
|
+
}
|
|
461
|
+
else if (go.isBytesType(field.type)) {
|
|
462
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
463
|
+
unmarshalBody += '\t\tif val != nil && string(val) != "null" {\n';
|
|
464
|
+
unmarshalBody += `\t\t\t\terr = runtime.DecodeByteArray(string(val), &${receiver}.${field.name}, runtime.Base64${field.type.encoding}Format)\n\t\t}\n`;
|
|
465
|
+
}
|
|
466
|
+
else if (go.isSliceType(field.type) && go.isBytesType(field.type.elementType)) {
|
|
467
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
468
|
+
unmarshalBody += '\t\t\tvar encodedValue []string\n';
|
|
469
|
+
unmarshalBody += `\t\t\terr = unpopulate(val, "${field.name}", &encodedValue)\n`;
|
|
470
|
+
unmarshalBody += '\t\t\tif err == nil && len(encodedValue) > 0 {\n';
|
|
471
|
+
unmarshalBody += `\t\t\t\t${receiver}.${field.name} = make([][]byte, len(encodedValue))\n`;
|
|
472
|
+
unmarshalBody += '\t\t\t\tfor i := 0; i < len(encodedValue) && err == nil; i++ {\n';
|
|
473
|
+
unmarshalBody += `\t\t\t\t\terr = runtime.DecodeByteArray(encodedValue[i], &${receiver}.${field.name}[i], runtime.Base64${field.type.elementType.encoding}Format)\n`;
|
|
474
|
+
unmarshalBody += '\t\t\t\t}\n\t\t\t}\n';
|
|
475
|
+
modelDef.SerDe.needsJSONUnpopulate = true;
|
|
476
|
+
}
|
|
477
|
+
else if (go.isSliceType(field.type) && field.type.rawJSONAsBytes) {
|
|
478
|
+
unmarshalBody += '\t\t\tif string(val) != "null" {\n';
|
|
479
|
+
unmarshalBody += `\t\t\t\t${receiver}.${field.name} = val\n\t\t\t}\n`;
|
|
480
|
+
}
|
|
481
|
+
else {
|
|
482
|
+
unmarshalBody += `\t\t\t\terr = unpopulate(val, "${field.name}", &${receiver}.${field.name})\n`;
|
|
483
|
+
modelDef.SerDe.needsJSONUnpopulate = true;
|
|
484
|
+
}
|
|
485
|
+
unmarshalBody += '\t\t\tdelete(rawMsg, key)\n';
|
|
486
|
+
}
|
|
487
|
+
if (addlProps) {
|
|
488
|
+
unmarshalBody += '\t\tdefault:\n';
|
|
489
|
+
unmarshalBody += emitAddlProps('\t', addlProps);
|
|
490
|
+
}
|
|
491
|
+
else if (options.disallowUnknownFields) {
|
|
492
|
+
unmarshalBody += '\t\tdefault:\n';
|
|
493
|
+
unmarshalBody += `\t\t\terr = fmt.Errorf("unmarshalling type %T, unknown field %q", ${receiver}, key)\n`;
|
|
494
|
+
}
|
|
495
|
+
unmarshalBody += '\t\t}\n';
|
|
496
|
+
unmarshalBody += '\t\tif err != nil {\n';
|
|
497
|
+
unmarshalBody += `\t\t\treturn fmt.Errorf("unmarshalling type %T: %v", ${receiver}, err)\n`;
|
|
498
|
+
unmarshalBody += '\t\t}\n';
|
|
499
|
+
unmarshalBody += '\t}\n'; // end for key, val := range rawMsg
|
|
500
|
+
unmarshalBody += '\treturn nil\n';
|
|
501
|
+
return unmarshalBody;
|
|
502
|
+
}
|
|
503
|
+
// returns true if item has a discriminator interface.
|
|
504
|
+
// recursively called for arrays and dictionaries.
|
|
505
|
+
function hasDiscriminatorInterface(item) {
|
|
506
|
+
if (go.isInterfaceType(item)) {
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
else if (go.isMapType(item)) {
|
|
510
|
+
return hasDiscriminatorInterface(item.valueType);
|
|
511
|
+
}
|
|
512
|
+
else if (go.isSliceType(item)) {
|
|
513
|
+
return hasDiscriminatorInterface(item.elementType);
|
|
514
|
+
}
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
// returns the text for unmarshalling a discriminated type
|
|
518
|
+
function generateDiscriminatorUnmarshaller(field, receiver) {
|
|
519
|
+
const startingIndentation = '\t\t\t';
|
|
520
|
+
const propertyName = field.name;
|
|
521
|
+
// these are the simple, non-nested cases (e.g. IterfaceType, []InterfaceType, map[string]InterfaceType)
|
|
522
|
+
if (go.isInterfaceType(field.type)) {
|
|
523
|
+
return `${startingIndentation}${receiver}.${propertyName}, err = unmarshal${field.type.name}(val)\n`;
|
|
524
|
+
}
|
|
525
|
+
else if (go.isSliceType(field.type) && go.isInterfaceType(field.type.elementType)) {
|
|
526
|
+
return `${startingIndentation}${receiver}.${propertyName}, err = unmarshal${field.type.elementType.name}Array(val)\n`;
|
|
527
|
+
}
|
|
528
|
+
else if (go.isMapType(field.type) && go.isInterfaceType(field.type.valueType)) {
|
|
529
|
+
return `${startingIndentation}${receiver}.${propertyName}, err = unmarshal${field.type.valueType.name}Map(val)\n`;
|
|
530
|
+
}
|
|
531
|
+
// nested case (e.g. [][]InterfaceType, map[string]map[string]InterfaceType etc)
|
|
532
|
+
// first, unmarshal the raw data
|
|
533
|
+
const rawTargetVar = `${field.serializedName}Raw`;
|
|
534
|
+
let text = `${startingIndentation}var ${rawTargetVar} ${recursiveGetDiscriminatorTypeName(field.type, true)}\n`;
|
|
535
|
+
text += `${startingIndentation}if err = json.Unmarshal(val, &${rawTargetVar}); err != nil {\n`;
|
|
536
|
+
text += `${startingIndentation}\treturn err\n${startingIndentation}}\n`;
|
|
537
|
+
// create a local instantiation of the final type
|
|
538
|
+
const finalTargetVar = field.serializedName;
|
|
539
|
+
let finalTargetCtor = recursiveGetDiscriminatorTypeName(field.type, false);
|
|
540
|
+
if (go.isSliceType(field.type)) {
|
|
541
|
+
finalTargetCtor = `make(${finalTargetCtor}, len(${rawTargetVar}))`;
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
// must be a dictionary
|
|
545
|
+
finalTargetCtor = `${finalTargetCtor}{}`;
|
|
546
|
+
}
|
|
547
|
+
text += `${startingIndentation}${finalTargetVar} := ${finalTargetCtor}\n`;
|
|
548
|
+
// now populate the final type
|
|
549
|
+
text += recursivePopulateDiscriminator(field.type, receiver, rawTargetVar, finalTargetVar, startingIndentation, 1);
|
|
550
|
+
// finally, assign the final target to the property
|
|
551
|
+
text += `${startingIndentation}${receiver}.${propertyName} = ${finalTargetVar}\n`;
|
|
552
|
+
return text;
|
|
553
|
+
}
|
|
554
|
+
// constructs the type name for a nested discriminated type
|
|
555
|
+
// raw e.g. map[string]json.RawMessage, []json.RawMessage etc
|
|
556
|
+
// !raw e.g. map[string]map[string]InterfaceType, [][]InterfaceType etc
|
|
557
|
+
function recursiveGetDiscriminatorTypeName(item, raw) {
|
|
558
|
+
// when raw is true, stop recursing at the level before the leaf schema
|
|
559
|
+
if (go.isSliceType(item)) {
|
|
560
|
+
if (!raw || !go.isInterfaceType(item.elementType)) {
|
|
561
|
+
return `[]${recursiveGetDiscriminatorTypeName(item.elementType, raw)}`;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
else if (go.isMapType(item)) {
|
|
565
|
+
if (!raw || !go.isInterfaceType(item.valueType)) {
|
|
566
|
+
return `map[string]${recursiveGetDiscriminatorTypeName(item.valueType, raw)}`;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
if (raw) {
|
|
570
|
+
return 'json.RawMessage';
|
|
571
|
+
}
|
|
572
|
+
return go.getTypeDeclaration(item);
|
|
573
|
+
}
|
|
574
|
+
// recursively constructs the text to populate a nested discriminator
|
|
575
|
+
function recursivePopulateDiscriminator(item, receiver, rawSrc, dest, indent, nesting) {
|
|
576
|
+
let text = '';
|
|
577
|
+
let interfaceName = '';
|
|
578
|
+
let targetType = '';
|
|
579
|
+
if (go.isSliceType(item)) {
|
|
580
|
+
if (!go.isInterfaceType(item.elementType)) {
|
|
581
|
+
if (nesting > 1) {
|
|
582
|
+
// at nestling level 1, the destination var was already created in generateDiscriminatorUnmarshaller()
|
|
583
|
+
text += `${indent}${dest} = make(${recursiveGetDiscriminatorTypeName(item, false)}, len(${rawSrc}))\n`;
|
|
584
|
+
}
|
|
585
|
+
text += `${indent}for i${nesting} := range ${rawSrc} {\n`;
|
|
586
|
+
rawSrc = `${rawSrc}[i${nesting}]`; // source becomes each element in the source slice
|
|
587
|
+
dest = `${dest}[i${nesting}]`; // update destination to each element in the destination slice
|
|
588
|
+
text += recursivePopulateDiscriminator(item.elementType, receiver, rawSrc, dest, indent + '\t', nesting + 1);
|
|
589
|
+
text += `${indent}}\n`;
|
|
590
|
+
return text;
|
|
591
|
+
}
|
|
592
|
+
// we're at leaf node - 1, so get the interface from the element's type
|
|
593
|
+
interfaceName = go.getTypeDeclaration(item.elementType);
|
|
594
|
+
targetType = 'Array';
|
|
595
|
+
}
|
|
596
|
+
else if (go.isMapType(item)) {
|
|
597
|
+
if (!go.isInterfaceType(item.valueType)) {
|
|
598
|
+
if (nesting > 1) {
|
|
599
|
+
// at nestling level 1, the destination var was already created in generateDiscriminatorUnmarshaller()
|
|
600
|
+
text += `${indent}${dest} = ${recursiveGetDiscriminatorTypeName(item, false)}{}\n`;
|
|
601
|
+
}
|
|
602
|
+
text += `${indent}for k${nesting}, v${nesting} := range ${rawSrc} {\n`;
|
|
603
|
+
rawSrc = `v${nesting}`; // source becomes the current value in the source map
|
|
604
|
+
dest = `${dest}[k${nesting}]`; // update destination to the destination map's value for the current key
|
|
605
|
+
text += recursivePopulateDiscriminator(item.valueType, receiver, rawSrc, dest, indent + '\t', nesting + 1);
|
|
606
|
+
text += `${indent}}\n`;
|
|
607
|
+
return text;
|
|
608
|
+
}
|
|
609
|
+
// we're at leaf node - 1, so get the interface from the element's type
|
|
610
|
+
interfaceName = go.getTypeDeclaration(item.valueType);
|
|
611
|
+
targetType = 'Map';
|
|
612
|
+
}
|
|
613
|
+
text += `${indent}${dest}, err = unmarshal${interfaceName}${targetType}(${rawSrc})\n`;
|
|
614
|
+
text += `${indent}if err != nil {\n${indent}\treturn fmt.Errorf("unmarshalling type %T: %v", ${receiver}, err)\n${indent}}\n`;
|
|
615
|
+
return text;
|
|
616
|
+
}
|
|
617
|
+
function generateXMLMarshaller(modelType, modelDef, imports) {
|
|
618
|
+
var _a;
|
|
619
|
+
// only needed for types with time.Time or where the XML name doesn't match the type name
|
|
620
|
+
const receiver = modelDef.receiverName();
|
|
621
|
+
const desc = `MarshalXML implements the xml.Marshaller interface for type ${modelDef.Name}.`;
|
|
622
|
+
let text = `func (${receiver} ${modelDef.Name}) MarshalXML(enc *xml.Encoder, start xml.StartElement) error {\n`;
|
|
623
|
+
if ((_a = modelType.xml) === null || _a === void 0 ? void 0 : _a.wrapper) {
|
|
624
|
+
text += `\tstart.Name.Local = "${modelType.xml.wrapper}"\n`;
|
|
625
|
+
}
|
|
626
|
+
text += generateAliasType(modelType, receiver, true);
|
|
627
|
+
for (const field of values(modelDef.Fields)) {
|
|
628
|
+
if (go.isSliceType(field.type)) {
|
|
629
|
+
text += `\tif ${receiver}.${field.name} != nil {\n`;
|
|
630
|
+
text += `\t\taux.${field.name} = &${receiver}.${field.name}\n`;
|
|
631
|
+
text += '\t}\n';
|
|
632
|
+
}
|
|
633
|
+
else if (go.isBytesType(field.type)) {
|
|
634
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
635
|
+
text += `\tif ${receiver}.${field.name} != nil {\n`;
|
|
636
|
+
text += `\t\tencoded${field.name} := runtime.EncodeByteArray(${receiver}.${field.name}, runtime.Base64${field.type.encoding}Format)\n`;
|
|
637
|
+
text += `\t\taux.${field.name} = &encoded${field.name}\n`;
|
|
638
|
+
text += '\t}\n';
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
text += '\treturn enc.EncodeElement(aux, start)\n';
|
|
642
|
+
text += '}\n\n';
|
|
643
|
+
modelDef.SerDe.methods.push({ name: 'MarshalXML', desc: desc, text: text });
|
|
644
|
+
}
|
|
645
|
+
function generateXMLUnmarshaller(modelType, modelDef, imports) {
|
|
646
|
+
// non-polymorphic case, must be something with time.Time
|
|
647
|
+
const receiver = modelDef.receiverName();
|
|
648
|
+
const desc = `UnmarshalXML implements the xml.Unmarshaller interface for type ${modelDef.Name}.`;
|
|
649
|
+
let text = `func (${receiver} *${modelDef.Name}) UnmarshalXML(dec *xml.Decoder, start xml.StartElement) error {\n`;
|
|
650
|
+
text += generateAliasType(modelType, receiver, false);
|
|
651
|
+
text += '\tif err := dec.DecodeElement(aux, &start); err != nil {\n';
|
|
652
|
+
text += '\t\treturn err\n';
|
|
653
|
+
text += '\t}\n';
|
|
654
|
+
for (const field of values(modelDef.Fields)) {
|
|
655
|
+
if (go.isTimeType(field.type)) {
|
|
656
|
+
text += `\tif aux.${field.name} != nil && !(*time.Time)(aux.${field.name}).IsZero() {\n`;
|
|
657
|
+
text += `\t\t${receiver}.${field.name} = (*time.Time)(aux.${field.name})\n\t}\n`;
|
|
658
|
+
}
|
|
659
|
+
else if (field.annotations.isAdditionalProperties || go.isMapType(field.type)) {
|
|
660
|
+
text += `\t${receiver}.${field.name} = (map[string]*string)(aux.${field.name})\n`;
|
|
661
|
+
}
|
|
662
|
+
else if (go.isBytesType(field.type)) {
|
|
663
|
+
imports.add('github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime');
|
|
664
|
+
text += `\tif aux.${field.name} != nil {\n`;
|
|
665
|
+
text += `\t\tif err := runtime.DecodeByteArray(*aux.${field.name}, &${receiver}.${field.name}, runtime.Base64${field.type.encoding}Format); err != nil {\n`;
|
|
666
|
+
text += '\t\t\treturn err\n';
|
|
667
|
+
text += '\t\t}\n';
|
|
668
|
+
text += '\t}\n';
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
text += '\treturn nil\n';
|
|
672
|
+
text += '}\n\n';
|
|
673
|
+
modelDef.SerDe.methods.push({ name: 'UnmarshalXML', desc: desc, text: text });
|
|
674
|
+
}
|
|
675
|
+
// generates an alias type used by custom XML marshaller/unmarshaller
|
|
676
|
+
function generateAliasType(modelType, receiver, forMarshal) {
|
|
677
|
+
let text = `\ttype alias ${modelType.name}\n`;
|
|
678
|
+
text += '\taux := &struct {\n';
|
|
679
|
+
text += '\t\t*alias\n';
|
|
680
|
+
for (const field of values(modelType.fields)) {
|
|
681
|
+
const sn = getXMLSerialization(field, false);
|
|
682
|
+
if (go.isTimeType(field.type)) {
|
|
683
|
+
text += `\t\t${field.name} *${field.type.dateTimeFormat} \`${modelType.format}:"${sn}"\`\n`;
|
|
684
|
+
}
|
|
685
|
+
else if (field.annotations.isAdditionalProperties || go.isMapType(field.type)) {
|
|
686
|
+
text += `\t\t${field.name} additionalProperties \`${modelType.format}:"${sn}"\`\n`;
|
|
687
|
+
}
|
|
688
|
+
else if (go.isSliceType(field.type)) {
|
|
689
|
+
text += `\t\t${field.name} *${go.getTypeDeclaration(field.type)} \`${modelType.format}:"${sn}"\`\n`;
|
|
690
|
+
}
|
|
691
|
+
else if (go.isBytesType(field.type)) {
|
|
692
|
+
text += `\t\t${field.name} *string \`${modelType.format}:"${sn}"\`\n`;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
text += '\t}{\n';
|
|
696
|
+
let rec = receiver;
|
|
697
|
+
if (forMarshal) {
|
|
698
|
+
rec = '&' + rec;
|
|
699
|
+
}
|
|
700
|
+
text += `\t\talias: (*alias)(${rec}),\n`;
|
|
701
|
+
if (forMarshal) {
|
|
702
|
+
// emit code to initialize time fields
|
|
703
|
+
for (const field of modelType.fields) {
|
|
704
|
+
if (!go.isTimeType(field.type)) {
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
text += `\t\t${field.name}: (*${field.type.dateTimeFormat})(${receiver}.${field.name}),\n`;
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
text += '\t}\n';
|
|
711
|
+
return text;
|
|
712
|
+
}
|
|
713
|
+
class SerDeInfo {
|
|
714
|
+
constructor() {
|
|
715
|
+
this.methods = new Array();
|
|
716
|
+
this.needsJSONPopulate = false;
|
|
717
|
+
this.needsJSONUnpopulate = false;
|
|
718
|
+
this.needsJSONPopulateByteArray = false;
|
|
719
|
+
this.needsJSONPopulateAny = false;
|
|
720
|
+
this.needsJSONPopulateMultipart = false;
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
// represents model definition as a Go struct
|
|
724
|
+
class ModelDef {
|
|
725
|
+
constructor(name, format, fields, description) {
|
|
726
|
+
this.Name = name;
|
|
727
|
+
this.Format = format;
|
|
728
|
+
this.Description = description;
|
|
729
|
+
this.Fields = fields;
|
|
730
|
+
this.SerDe = new SerDeInfo();
|
|
731
|
+
this.Methods = new Array();
|
|
732
|
+
}
|
|
733
|
+
text() {
|
|
734
|
+
var _a;
|
|
735
|
+
let text = '';
|
|
736
|
+
if (this.Description) {
|
|
737
|
+
text += `${comment(this.Description, '// ', undefined, commentLength)}\n`;
|
|
738
|
+
}
|
|
739
|
+
text += `type ${this.Name} struct {\n`;
|
|
740
|
+
// group fields by required/optional/read-only in that order
|
|
741
|
+
(_a = this.Fields) === null || _a === void 0 ? void 0 : _a.sort((lhs, rhs) => {
|
|
742
|
+
if ((lhs.annotations.required && !rhs.annotations.required) || (!lhs.annotations.readOnly && rhs.annotations.readOnly)) {
|
|
743
|
+
return -1;
|
|
744
|
+
}
|
|
745
|
+
else if ((rhs.annotations.readOnly && !lhs.annotations.readOnly) || (!rhs.annotations.readOnly && lhs.annotations.readOnly)) {
|
|
746
|
+
return 1;
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
return 0;
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
// used to track when to add an extra \n between fields that have comments
|
|
753
|
+
let first = true;
|
|
754
|
+
for (const field of values(this.Fields)) {
|
|
755
|
+
if (field.description) {
|
|
756
|
+
if (!first) {
|
|
757
|
+
// add an extra new-line between fields IFF the field
|
|
758
|
+
// has a comment and it's not the very first one.
|
|
759
|
+
text += '\n';
|
|
760
|
+
}
|
|
761
|
+
text += `\t${comment(field.description, '// ', undefined, commentLength)}\n`;
|
|
762
|
+
}
|
|
763
|
+
let typeName = go.getTypeDeclaration(field.type);
|
|
764
|
+
if (go.isLiteralValue(field.type)) {
|
|
765
|
+
// for constants we use the underlying type name
|
|
766
|
+
typeName = go.getLiteralValueTypeName(field.type.type);
|
|
767
|
+
}
|
|
768
|
+
let serialization = field.serializedName;
|
|
769
|
+
if (this.Format === 'json') {
|
|
770
|
+
serialization += ',omitempty';
|
|
771
|
+
}
|
|
772
|
+
else if (this.Format === 'xml') {
|
|
773
|
+
serialization = getXMLSerialization(field, false);
|
|
774
|
+
}
|
|
775
|
+
let tag = '';
|
|
776
|
+
// only emit tags for XML; JSON uses custom marshallers/unmarshallers
|
|
777
|
+
if (this.Format === 'xml' && !field.annotations.isAdditionalProperties) {
|
|
778
|
+
tag = ` \`${this.Format}:"${serialization}"\``;
|
|
779
|
+
}
|
|
780
|
+
text += `\t${field.name} ${getStar(field.byValue)}${typeName}${tag}\n`;
|
|
781
|
+
first = false;
|
|
782
|
+
}
|
|
783
|
+
text += '}\n\n';
|
|
784
|
+
return text;
|
|
785
|
+
}
|
|
786
|
+
receiverName() {
|
|
787
|
+
const typeName = this.Name;
|
|
788
|
+
return typeName[0].toLowerCase();
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
export function getXMLSerialization(field, isResponseEnvelope) {
|
|
792
|
+
var _a, _b, _c, _d, _e;
|
|
793
|
+
let serialization = field.serializedName;
|
|
794
|
+
// default to using the serialization name
|
|
795
|
+
if ((_a = field.xml) === null || _a === void 0 ? void 0 : _a.name) {
|
|
796
|
+
// xml can specifiy its own name, prefer that if available
|
|
797
|
+
serialization = field.xml.name;
|
|
798
|
+
}
|
|
799
|
+
else if ((_b = field.xml) === null || _b === void 0 ? void 0 : _b.text) {
|
|
800
|
+
// type has the x-ms-text attribute applied so it should be character data, not a node (https://github.com/Azure/autorest/tree/main/docs/extensions#x-ms-text)
|
|
801
|
+
// see https://pkg.go.dev/encoding/xml#Unmarshal for what ,chardata actually means
|
|
802
|
+
serialization = ',chardata';
|
|
803
|
+
}
|
|
804
|
+
if ((_c = field.xml) === null || _c === void 0 ? void 0 : _c.attribute) {
|
|
805
|
+
// value comes from an xml attribute
|
|
806
|
+
serialization += ',attr';
|
|
807
|
+
}
|
|
808
|
+
else if (go.isSliceType(field.type)) {
|
|
809
|
+
// start with the serialized name of the element, preferring xml name if available
|
|
810
|
+
let inner = go.getTypeDeclaration(field.type.elementType);
|
|
811
|
+
if ((_d = field.xml) === null || _d === void 0 ? void 0 : _d.name) {
|
|
812
|
+
inner = field.xml.name;
|
|
813
|
+
}
|
|
814
|
+
// arrays can be wrapped or unwrapped. here's a wrapped example
|
|
815
|
+
// note how the array of apple objects is "wrapped" in GoodApples
|
|
816
|
+
// <AppleBarrel>
|
|
817
|
+
// <GoodApples>
|
|
818
|
+
// <Apple>Fuji</Apple>
|
|
819
|
+
// <Apple>Gala</Apple>
|
|
820
|
+
// </GoodApples>
|
|
821
|
+
// </AppleBarrel>
|
|
822
|
+
// here's an unwrapped example, the array of slide objects
|
|
823
|
+
// is embedded directly in the object (no "wrapping")
|
|
824
|
+
// <slideshow>
|
|
825
|
+
// <slide>
|
|
826
|
+
// <title>Wake up to WonderWidgets!</title>
|
|
827
|
+
// </slide>
|
|
828
|
+
// <slide>
|
|
829
|
+
// <title>Overview</title>
|
|
830
|
+
// </slide>
|
|
831
|
+
// </slideshow>
|
|
832
|
+
// arrays in the response type are handled slightly different as we
|
|
833
|
+
// unmarshal directly into them so no need to add the unwrapping.
|
|
834
|
+
if (((_e = field.xml) === null || _e === void 0 ? void 0 : _e.wraps) && !isResponseEnvelope) {
|
|
835
|
+
serialization += `>${field.xml.wraps}`;
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
serialization = inner;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
return serialization;
|
|
842
|
+
}
|
|
843
|
+
export function getStar(byValue) {
|
|
844
|
+
if (byValue === true) {
|
|
845
|
+
return '';
|
|
846
|
+
}
|
|
847
|
+
return '*';
|
|
848
|
+
}
|
|
849
|
+
//# sourceMappingURL=models.js.map
|