@xrpckit/target-go-server 0.0.1 → 0.0.3
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/index.d.ts +267 -36
- package/dist/index.js +1511 -310
- package/package.json +21 -5
- package/src/generator.ts +204 -0
- package/src/go-builder.ts +148 -0
- package/src/index.ts +15 -0
- package/src/patterns.test.ts +114 -0
- package/src/patterns.ts +284 -0
- package/src/server-generator.ts +233 -0
- package/src/type-collector.ts +222 -0
- package/src/type-generator.ts +353 -0
- package/src/type-mapper.ts +305 -0
- package/src/validation-generator.ts +851 -0
- package/src/validation-mapper.ts +260 -0
package/package.json
CHANGED
|
@@ -1,13 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xrpckit/target-go-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Go server code generator for xRPC - generates idiomatic Go HTTP handlers from contracts",
|
|
4
5
|
"type": "module",
|
|
5
6
|
"license": "MIT",
|
|
7
|
+
"author": "mwesox",
|
|
6
8
|
"repository": {
|
|
7
9
|
"type": "git",
|
|
8
|
-
"url": "https://github.com/mwesox/xrpc.git",
|
|
10
|
+
"url": "git+https://github.com/mwesox/xrpc.git",
|
|
9
11
|
"directory": "packages/target-go-server"
|
|
10
12
|
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/mwesox/xrpc/issues"
|
|
15
|
+
},
|
|
16
|
+
"homepage": "https://github.com/mwesox/xrpc#readme",
|
|
17
|
+
"keywords": [
|
|
18
|
+
"xrpc",
|
|
19
|
+
"rpc",
|
|
20
|
+
"go",
|
|
21
|
+
"golang",
|
|
22
|
+
"server",
|
|
23
|
+
"codegen",
|
|
24
|
+
"api",
|
|
25
|
+
"type-safe"
|
|
26
|
+
],
|
|
11
27
|
"publishConfig": {
|
|
12
28
|
"access": "public"
|
|
13
29
|
},
|
|
@@ -21,14 +37,14 @@
|
|
|
21
37
|
}
|
|
22
38
|
},
|
|
23
39
|
"files": [
|
|
24
|
-
"dist"
|
|
40
|
+
"dist",
|
|
41
|
+
"src"
|
|
25
42
|
],
|
|
26
43
|
"scripts": {
|
|
27
44
|
"build": "tsup src/index.ts --format esm --dts --clean"
|
|
28
45
|
},
|
|
29
46
|
"dependencies": {
|
|
30
|
-
"@xrpckit/
|
|
31
|
-
"@xrpckit/parser": "^0.0.1"
|
|
47
|
+
"@xrpckit/sdk": "^0.0.3"
|
|
32
48
|
},
|
|
33
49
|
"devDependencies": {
|
|
34
50
|
"@types/node": "^22.0.0",
|
package/src/generator.ts
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type ContractDefinition,
|
|
3
|
+
type Property,
|
|
4
|
+
type Target,
|
|
5
|
+
type TargetInput,
|
|
6
|
+
type TargetOutput,
|
|
7
|
+
type TargetSupport,
|
|
8
|
+
TYPE_KINDS,
|
|
9
|
+
type TypeDefinition,
|
|
10
|
+
type TypeReference,
|
|
11
|
+
toPascalCase,
|
|
12
|
+
VALIDATION_KINDS,
|
|
13
|
+
validateSupport,
|
|
14
|
+
} from "@xrpckit/sdk";
|
|
15
|
+
import { GoServerGenerator } from "./server-generator";
|
|
16
|
+
import { GoTypeCollector } from "./type-collector";
|
|
17
|
+
import { GoTypeGenerator } from "./type-generator";
|
|
18
|
+
import { GoValidationGenerator } from "./validation-generator";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Go server code generator that produces idiomatic Go HTTP handlers from xRPC contracts.
|
|
22
|
+
*
|
|
23
|
+
* Generates three files:
|
|
24
|
+
* - types.go: Struct definitions, handler types, middleware types
|
|
25
|
+
* - router.go: HTTP routing and JSON handling
|
|
26
|
+
* - validation.go: Input validation functions
|
|
27
|
+
*/
|
|
28
|
+
const support: TargetSupport = {
|
|
29
|
+
supportedTypes: [...TYPE_KINDS],
|
|
30
|
+
supportedValidations: [...VALIDATION_KINDS],
|
|
31
|
+
notes: [
|
|
32
|
+
"Generates idiomatic Go code using standard library only",
|
|
33
|
+
"Uses net/http for HTTP handling",
|
|
34
|
+
"Uses encoding/json for JSON marshaling",
|
|
35
|
+
"Validation uses net/mail for email, net/url for URLs, regexp for patterns",
|
|
36
|
+
],
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function getPackageName(options?: Record<string, unknown>): string {
|
|
40
|
+
if (
|
|
41
|
+
options &&
|
|
42
|
+
typeof options.packageName === "string" &&
|
|
43
|
+
options.packageName
|
|
44
|
+
) {
|
|
45
|
+
return options.packageName;
|
|
46
|
+
}
|
|
47
|
+
return "server";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isNullableType(typeRef: TypeReference): boolean {
|
|
51
|
+
if (typeRef.kind === "nullable") {
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (typeRef.kind === "optional" && typeof typeRef.baseType === "object") {
|
|
56
|
+
return isNullableType(typeRef.baseType);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (typeRef.kind === "union" && typeRef.unionTypes) {
|
|
60
|
+
const nonNullVariants = typeRef.unionTypes.filter(
|
|
61
|
+
(variant) =>
|
|
62
|
+
!(variant.kind === "literal" && variant.literalValue === null),
|
|
63
|
+
);
|
|
64
|
+
return nonNullVariants.length === 1;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function collectRequiredNullableFields(contract: ContractDefinition): string[] {
|
|
71
|
+
const results = new Set<string>();
|
|
72
|
+
const visited = new Set<string>();
|
|
73
|
+
|
|
74
|
+
const collectFromProperties = (
|
|
75
|
+
properties: Property[],
|
|
76
|
+
parentName: string,
|
|
77
|
+
): void => {
|
|
78
|
+
for (const prop of properties) {
|
|
79
|
+
const propPath = `${parentName}.${prop.name}`;
|
|
80
|
+
if (prop.required && isNullableType(prop.type)) {
|
|
81
|
+
results.add(propPath);
|
|
82
|
+
}
|
|
83
|
+
collectFromTypeRef(prop.type, `${parentName}${toPascalCase(prop.name)}`);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const collectFromTypeRef = (
|
|
88
|
+
typeRef: TypeReference,
|
|
89
|
+
contextName: string,
|
|
90
|
+
): void => {
|
|
91
|
+
if (typeRef.kind === "optional" || typeRef.kind === "nullable") {
|
|
92
|
+
if (typeof typeRef.baseType === "object") {
|
|
93
|
+
collectFromTypeRef(typeRef.baseType, contextName);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (typeRef.kind === "object" && typeRef.properties) {
|
|
99
|
+
const typeName = typeRef.name ? toPascalCase(typeRef.name) : contextName;
|
|
100
|
+
collectFromProperties(typeRef.properties, typeName);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (typeRef.kind === "array" && typeRef.elementType) {
|
|
105
|
+
collectFromTypeRef(typeRef.elementType, `${contextName}Item`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (typeRef.kind === "record" && typeRef.valueType) {
|
|
110
|
+
collectFromTypeRef(typeRef.valueType, `${contextName}Value`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (typeRef.kind === "tuple" && typeRef.tupleElements) {
|
|
115
|
+
typeRef.tupleElements.forEach((elem, index) => {
|
|
116
|
+
collectFromTypeRef(elem, `${contextName}V${index}`);
|
|
117
|
+
});
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (typeRef.kind === "union" && typeRef.unionTypes) {
|
|
122
|
+
typeRef.unionTypes.forEach((variant, index) => {
|
|
123
|
+
collectFromTypeRef(variant, `${contextName}Variant${index}`);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const collectFromTypeDefinition = (
|
|
129
|
+
typeDef: TypeDefinition,
|
|
130
|
+
typeName: string,
|
|
131
|
+
): void => {
|
|
132
|
+
if (visited.has(typeName)) return;
|
|
133
|
+
visited.add(typeName);
|
|
134
|
+
|
|
135
|
+
if (typeDef.properties) {
|
|
136
|
+
collectFromProperties(typeDef.properties, typeName);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (typeDef.kind === "array" && typeDef.elementType) {
|
|
140
|
+
collectFromTypeRef(typeDef.elementType, `${typeName}Item`);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
for (const type of contract.types) {
|
|
145
|
+
collectFromTypeDefinition(type, toPascalCase(type.name));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for (const endpoint of contract.endpoints) {
|
|
149
|
+
collectFromTypeRef(endpoint.input, `${endpoint.fullName}.input`);
|
|
150
|
+
collectFromTypeRef(endpoint.output, `${endpoint.fullName}.output`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return Array.from(results);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function generateGoServer(input: TargetInput): TargetOutput {
|
|
157
|
+
const { contract } = input;
|
|
158
|
+
const diagnostics = validateSupport(contract, support, "go-server");
|
|
159
|
+
const requiredNullableFields = collectRequiredNullableFields(contract);
|
|
160
|
+
for (const field of requiredNullableFields) {
|
|
161
|
+
diagnostics.push({
|
|
162
|
+
severity: "warning",
|
|
163
|
+
message: `Field "${field}" is required and nullable. Go cannot distinguish missing values from null, so validation only runs when the value is present.`,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const hasErrors = diagnostics.some((issue) => issue.severity === "error");
|
|
167
|
+
if (hasErrors) {
|
|
168
|
+
return { files: [], diagnostics };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const packageName = getPackageName(input.options);
|
|
172
|
+
const typeCollector = new GoTypeCollector();
|
|
173
|
+
const collectedTypes = typeCollector.collectTypes(contract);
|
|
174
|
+
|
|
175
|
+
const typeGenerator = new GoTypeGenerator(packageName);
|
|
176
|
+
const serverGenerator = new GoServerGenerator(packageName);
|
|
177
|
+
const validationGenerator = new GoValidationGenerator(packageName);
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
files: [
|
|
181
|
+
{
|
|
182
|
+
path: "types.go",
|
|
183
|
+
content: typeGenerator.generateTypes(contract, collectedTypes),
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
path: "router.go",
|
|
187
|
+
content: serverGenerator.generateServer(contract),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
path: "validation.go",
|
|
191
|
+
content: validationGenerator.generateValidation(
|
|
192
|
+
contract,
|
|
193
|
+
collectedTypes,
|
|
194
|
+
),
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
diagnostics,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export const goTarget: Target = {
|
|
202
|
+
name: "go-server",
|
|
203
|
+
generate: generateGoServer,
|
|
204
|
+
};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { CodeWriter } from "@xrpckit/sdk";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Go-specific code builder with fluent DSL for common Go patterns
|
|
5
|
+
*/
|
|
6
|
+
export class GoBuilder extends CodeWriter {
|
|
7
|
+
// Short aliases
|
|
8
|
+
l(text: string): this {
|
|
9
|
+
return this.writeLine(text);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
n(): this {
|
|
13
|
+
return this.newLine();
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
i(): this {
|
|
17
|
+
return this.indent();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
u(): this {
|
|
21
|
+
return this.unindent();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Go-specific patterns
|
|
25
|
+
package(name: string): this {
|
|
26
|
+
return this.l(`package ${name}`).n();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
import(...packages: string[]): this {
|
|
30
|
+
if (packages.length === 0) return this;
|
|
31
|
+
if (packages.length === 1) {
|
|
32
|
+
return this.l(`import "${packages[0]}"`).n();
|
|
33
|
+
}
|
|
34
|
+
this.l("import (").i();
|
|
35
|
+
for (const pkg of packages) {
|
|
36
|
+
this.l(`"${pkg}"`);
|
|
37
|
+
}
|
|
38
|
+
return this.u().l(")").n();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
type(name: string, typeDef: string): this {
|
|
42
|
+
return this.l(`type ${name} ${typeDef}`).n();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
struct(name: string, fn: (b: this) => void): this {
|
|
46
|
+
this.l(`type ${name} struct {`).i();
|
|
47
|
+
fn(this);
|
|
48
|
+
return this.u().l("}").n();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Anonymous struct
|
|
52
|
+
anonStruct(fn: (b: this) => void): this {
|
|
53
|
+
this.l("struct {").i();
|
|
54
|
+
fn(this);
|
|
55
|
+
return this.u().l("}");
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
func(signature: string, fn?: (b: this) => void): this {
|
|
59
|
+
if (fn) {
|
|
60
|
+
this.write(`func ${signature} {`);
|
|
61
|
+
this.indent();
|
|
62
|
+
fn(this);
|
|
63
|
+
this.unindent();
|
|
64
|
+
this.writeLine("}");
|
|
65
|
+
this.newLine();
|
|
66
|
+
} else {
|
|
67
|
+
this.l(`func ${signature}`);
|
|
68
|
+
this.n();
|
|
69
|
+
}
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
method(
|
|
74
|
+
receiver: string,
|
|
75
|
+
name: string,
|
|
76
|
+
params: string,
|
|
77
|
+
returns: string,
|
|
78
|
+
fn: (b: this) => void,
|
|
79
|
+
): this {
|
|
80
|
+
const sig = returns
|
|
81
|
+
? `${name}(${params}) ${returns}`
|
|
82
|
+
: `${name}(${params})`;
|
|
83
|
+
return this.func(`(${receiver}) ${sig}`, fn);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if(condition: string, fn: (b: this) => void): this {
|
|
87
|
+
this.l(`if ${condition} {`).i();
|
|
88
|
+
fn(this);
|
|
89
|
+
return this.u().l("}");
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
ifErr(fn: (b: this) => void): this {
|
|
93
|
+
return this.if("err != nil", fn);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return(value?: string): this {
|
|
97
|
+
return value ? this.l(`return ${value}`) : this.l("return");
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
var(name: string, type?: string, value?: string): this {
|
|
101
|
+
if (type && value) {
|
|
102
|
+
return this.l(`var ${name} ${type} = ${value}`);
|
|
103
|
+
}
|
|
104
|
+
if (type) {
|
|
105
|
+
return this.l(`var ${name} ${type}`);
|
|
106
|
+
}
|
|
107
|
+
if (value) {
|
|
108
|
+
return this.l(`${name} := ${value}`);
|
|
109
|
+
}
|
|
110
|
+
return this.l(`var ${name}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Declare variable with type inference
|
|
114
|
+
decl(name: string, value: string): this {
|
|
115
|
+
return this.l(`${name} := ${value}`);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
switch(
|
|
119
|
+
value: string,
|
|
120
|
+
cases: Array<{ value: string; fn: (b: GoBuilder) => void }>,
|
|
121
|
+
defaultCase?: (b: GoBuilder) => void,
|
|
122
|
+
): this {
|
|
123
|
+
this.l(`switch ${value} {`).i();
|
|
124
|
+
for (const c of cases) {
|
|
125
|
+
this.l(`case ${c.value}:`).i();
|
|
126
|
+
c.fn(this);
|
|
127
|
+
this.u();
|
|
128
|
+
}
|
|
129
|
+
if (defaultCase) {
|
|
130
|
+
this.l("default:").i();
|
|
131
|
+
defaultCase(this);
|
|
132
|
+
this.u();
|
|
133
|
+
}
|
|
134
|
+
return this.u().l("}");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
comment(text: string): this {
|
|
138
|
+
return this.l(`// ${text}`);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
blockComment(lines: string[]): this {
|
|
142
|
+
this.l("/*");
|
|
143
|
+
for (const line of lines) {
|
|
144
|
+
this.l(` * ${line}`);
|
|
145
|
+
}
|
|
146
|
+
return this.l(" */");
|
|
147
|
+
}
|
|
148
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export { goTarget } from "./generator";
|
|
2
|
+
export { GoBuilder } from "./go-builder";
|
|
3
|
+
export {
|
|
4
|
+
createGoBigIntPattern,
|
|
5
|
+
createGoDatePattern,
|
|
6
|
+
createGoEnumPattern,
|
|
7
|
+
createGoTuplePattern,
|
|
8
|
+
createGoUnionPattern,
|
|
9
|
+
} from "./patterns";
|
|
10
|
+
export { GoServerGenerator } from "./server-generator";
|
|
11
|
+
export { type CollectedType, GoTypeCollector } from "./type-collector";
|
|
12
|
+
export { GoTypeGenerator } from "./type-generator";
|
|
13
|
+
export { GoTypeMapper } from "./type-mapper";
|
|
14
|
+
export { GoValidationGenerator } from "./validation-generator";
|
|
15
|
+
export { type GoValidationCode, GoValidationMapper } from "./validation-mapper";
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, expect, it } from "bun:test";
|
|
2
|
+
import {
|
|
3
|
+
createGoBigIntPattern,
|
|
4
|
+
createGoDatePattern,
|
|
5
|
+
createGoEnumPattern,
|
|
6
|
+
createGoTuplePattern,
|
|
7
|
+
createGoUnionPattern,
|
|
8
|
+
} from "./patterns";
|
|
9
|
+
|
|
10
|
+
describe("Go patterns", () => {
|
|
11
|
+
describe("createGoEnumPattern", () => {
|
|
12
|
+
it("should generate valid Go enum code", () => {
|
|
13
|
+
const utility = createGoEnumPattern("Status", [
|
|
14
|
+
"active",
|
|
15
|
+
"inactive",
|
|
16
|
+
"pending",
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
expect(utility.id).toBe("enum_Status");
|
|
20
|
+
expect(utility.code).toContain("type Status string");
|
|
21
|
+
expect(utility.code).toContain("StatusActive");
|
|
22
|
+
expect(utility.code).toContain("StatusInactive");
|
|
23
|
+
expect(utility.code).toContain("StatusPending");
|
|
24
|
+
expect(utility.code).toContain("func (e Status) IsValid() bool");
|
|
25
|
+
expect(utility.code).toContain(
|
|
26
|
+
"func ParseStatus(s string) (Status, error)",
|
|
27
|
+
);
|
|
28
|
+
expect(utility.includeOnce).toBe(true);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("should filter out non-string values", () => {
|
|
32
|
+
const utility = createGoEnumPattern("Mixed", ["a", 1, "b", 2] as any);
|
|
33
|
+
|
|
34
|
+
expect(utility.code).toContain("MixedA");
|
|
35
|
+
expect(utility.code).toContain("MixedB");
|
|
36
|
+
expect(utility.code).not.toContain("Mixed1");
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("createGoTuplePattern", () => {
|
|
41
|
+
it("should generate valid Go tuple struct", () => {
|
|
42
|
+
const utility = createGoTuplePattern("Coordinate", [
|
|
43
|
+
"float64",
|
|
44
|
+
"float64",
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
expect(utility.id).toBe("tuple_Coordinate");
|
|
48
|
+
expect(utility.code).toContain("type Coordinate struct");
|
|
49
|
+
expect(utility.code).toContain("V0 float64");
|
|
50
|
+
expect(utility.code).toContain("V1 float64");
|
|
51
|
+
expect(utility.code).toContain("MarshalJSON");
|
|
52
|
+
expect(utility.code).toContain("UnmarshalJSON");
|
|
53
|
+
expect(utility.imports).toContain("encoding/json");
|
|
54
|
+
expect(utility.imports).toContain("fmt");
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("should handle different element types", () => {
|
|
58
|
+
const utility = createGoTuplePattern("Mixed", ["string", "int", "bool"]);
|
|
59
|
+
|
|
60
|
+
expect(utility.code).toContain("V0 string");
|
|
61
|
+
expect(utility.code).toContain("V1 int");
|
|
62
|
+
expect(utility.code).toContain("V2 bool");
|
|
63
|
+
expect(utility.code).toContain("expected 3 elements");
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("createGoUnionPattern", () => {
|
|
68
|
+
it("should generate valid Go union wrapper", () => {
|
|
69
|
+
const utility = createGoUnionPattern("Result", ["string", "int"]);
|
|
70
|
+
|
|
71
|
+
expect(utility.id).toBe("union_Result");
|
|
72
|
+
expect(utility.code).toContain("type Result struct");
|
|
73
|
+
expect(utility.code).toContain("Value interface{}");
|
|
74
|
+
expect(utility.code).toContain("AsString");
|
|
75
|
+
expect(utility.code).toContain("AsInt");
|
|
76
|
+
expect(utility.imports).toContain("encoding/json");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("should handle pointer types", () => {
|
|
80
|
+
const utility = createGoUnionPattern("Optional", ["*string", "*int"]);
|
|
81
|
+
|
|
82
|
+
expect(utility.code).toContain("AsString");
|
|
83
|
+
expect(utility.code).toContain("AsInt");
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("createGoBigIntPattern", () => {
|
|
88
|
+
it("should generate BigInt wrapper code", () => {
|
|
89
|
+
const utility = createGoBigIntPattern();
|
|
90
|
+
|
|
91
|
+
expect(utility.id).toBe("bigint");
|
|
92
|
+
expect(utility.code).toContain("type BigInt struct");
|
|
93
|
+
expect(utility.code).toContain("*big.Int");
|
|
94
|
+
expect(utility.code).toContain("MarshalJSON");
|
|
95
|
+
expect(utility.code).toContain("UnmarshalJSON");
|
|
96
|
+
expect(utility.imports).toContain("math/big");
|
|
97
|
+
expect(utility.imports).toContain("fmt");
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("createGoDatePattern", () => {
|
|
102
|
+
it("should generate DateTime wrapper code", () => {
|
|
103
|
+
const utility = createGoDatePattern();
|
|
104
|
+
|
|
105
|
+
expect(utility.id).toBe("datetime");
|
|
106
|
+
expect(utility.code).toContain("type DateTime struct");
|
|
107
|
+
expect(utility.code).toContain("time.Time");
|
|
108
|
+
expect(utility.code).toContain("dateTimeFormats");
|
|
109
|
+
expect(utility.code).toContain("RFC3339");
|
|
110
|
+
expect(utility.imports).toContain("time");
|
|
111
|
+
expect(utility.imports).toContain("encoding/json");
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|