Skip to content

Commit

Permalink
feat(Golang): Add support for native golang Date/Time types (#2306)
Browse files Browse the repository at this point in the history
* feat(Golang): Add support for native golang Date/Time types

* code cleanup

* fix: import generation

* fix: proper way of indenting imports

* fixes: fix import generation, tests data, golang tests list

* fix: review issues

---------

Co-authored-by: Maciej Maczuga <[email protected]>
Co-authored-by: David Siegel <[email protected]>
  • Loading branch information
3 people authored Feb 14, 2024
1 parent f440afd commit 1f89a97
Show file tree
Hide file tree
Showing 18 changed files with 63,761 additions and 5,055 deletions.
118 changes: 107 additions & 11 deletions packages/quicktype-core/src/language/Golang.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { anyTypeIssueAnnotation, nullTypeIssueAnnotation } from "../Annotation";
import { TargetLanguage } from "../TargetLanguage";
import { ConvenienceRenderer } from "../ConvenienceRenderer";
import { RenderContext } from "../Renderer";
import { StringTypeMapping, TransformedStringTypeKind, PrimitiveStringTypeKind } from "..";

export const goOptions = {
justTypes: new BooleanOption("just-types", "Plain types only", false),
Expand Down Expand Up @@ -49,6 +50,12 @@ export class GoTargetLanguage extends TargetLanguage {
return true;
}

get stringTypeMapping(): StringTypeMapping {
const mapping: Map<TransformedStringTypeKind, PrimitiveStringTypeKind> = new Map();
mapping.set("date-time", "date-time");
return mapping;
}

get supportsOptionalClassProperties(): boolean {
return true;
}
Expand Down Expand Up @@ -85,7 +92,7 @@ const compoundTypeKinds: TypeKind[] = ["array", "class", "map", "enum"];

function isValueType(t: Type): boolean {
const kind = t.kind;
return primitiveValueTypeKinds.indexOf(kind) >= 0 || kind === "class" || kind === "enum";
return primitiveValueTypeKinds.indexOf(kind) >= 0 || kind === "class" || kind === "enum" || kind === "date-time";
}

function canOmitEmpty(cp: ClassProperty): boolean {
Expand Down Expand Up @@ -217,6 +224,13 @@ export class GoRenderer extends ConvenienceRenderer {
const nullable = nullableFromUnion(unionType);
if (nullable !== null) return this.nullableGoType(nullable, withIssues);
return this.nameForNamedType(unionType);
},
transformedStringType => {
if (transformedStringType.kind === "date-time") {
return "time.Time";
}

return "string";
}
);
}
Expand Down Expand Up @@ -264,8 +278,8 @@ export class GoRenderer extends ConvenienceRenderer {

private emitClass(c: ClassType, className: Name): void {
this.startFile(className);
this.emitPackageDefinitons(false);
let columns: Sourcelike[][] = [];
const usedTypes = new Set<string>();
this.forEachClassProperty(c, "none", (name, jsonName, p) => {
const description = this.descriptionForClassProperty(c, jsonName);
const docStrings =
Expand All @@ -283,7 +297,13 @@ export class GoRenderer extends ConvenienceRenderer {
[goType, " "],
["`", tags, "`"]
]);
usedTypes.add(goType.toString());
});

this.emitPackageDefinitons(
false,
usedTypes.has("time.Time") || usedTypes.has("*,time.Time") ? new Set<string>(["time"]) : undefined
);
this.emitDescription(this.descriptionForType(c));
this.emitStruct(className, columns);
this.endFile();
Expand Down Expand Up @@ -411,7 +431,7 @@ export class GoRenderer extends ConvenienceRenderer {
});
}

private emitPackageDefinitons(includeJSONEncodingImport: boolean): void {
private emitPackageDefinitons(includeJSONEncodingImport: boolean, imports: Set<string> = new Set<string>()): void {
if (!this._options.justTypes || this._options.justTypesAndPackage) {
this.ensureBlankLine();
const packageDeclaration = "package " + this._options.packageName;
Expand All @@ -420,27 +440,42 @@ export class GoRenderer extends ConvenienceRenderer {
}

if (!this._options.justTypes && !this._options.justTypesAndPackage) {
this.ensureBlankLine();
if (this.haveNamedUnions && this._options.multiFileOutput === false) {
this.emitLineOnce('import "bytes"');
this.emitLineOnce('import "errors"');
imports.add("bytes");
imports.add("errors");
}

if (includeJSONEncodingImport) {
this.emitLineOnce('import "encoding/json"');
imports.add("encoding/json");
}
this.ensureBlankLine();
}

this.emitImports(imports);
}

private emitImports(imports: Set<string>): void {
const sortedImports = Array.from(imports).sort((a, b) => a.localeCompare(b));

if (sortedImports.length === 0) {
return;
}

sortedImports.forEach(packageName => {
this.emitLineOnce(`import "${packageName}"`);
});
this.ensureBlankLine();
}

private emitHelperFunctions(): void {
if (this.haveNamedUnions) {
this.startFile("JSONSchemaSupport");
this.emitPackageDefinitons(true);
const imports = new Set<string>();
if (this._options.multiFileOutput) {
this.emitLineOnce('import "bytes"');
this.emitLineOnce('import "errors"');
imports.add("bytes");
imports.add("errors");
}

this.emitPackageDefinitons(true, imports);
this.ensureBlankLine();
this
.emitMultiline(`func unmarshalUnion(data []byte, pi **int64, pf **float64, pb **bool, ps **string, haveArray bool, pa interface{}, haveObject bool, pc interface{}, haveMap bool, pm interface{}, haveEnum bool, pe interface{}, nullable bool) (bool, error) {
Expand Down Expand Up @@ -568,6 +603,7 @@ func marshalUnion(pi *int64, pf *float64, pb *bool, ps *string, haveArray bool,
this.leadingComments === undefined
) {
this.emitSingleFileHeaderComments();
this.emitPackageDefinitons(false, this.collectAllImports());
}

this.forEachTopLevel(
Expand All @@ -587,4 +623,64 @@ func marshalUnion(pi *int64, pf *float64, pb *bool, ps *string, haveArray bool,

this.emitHelperFunctions();
}

private collectAllImports(): Set<string> {
let imports = new Set<string>();
this.forEachObject("leading-and-interposing", (c: ClassType, _className: Name) => {
const classImports = this.collectClassImports(c);
imports = new Set([...imports, ...classImports]);
});

this.forEachUnion("leading-and-interposing", (u: UnionType, _unionName: Name) => {
const unionImports = this.collectUnionImports(u);
imports = new Set([...imports, ...unionImports]);
});
return imports;
}

private collectClassImports(c: ClassType): Set<string> {
const usedTypes = new Set<string>();
const mapping: Map<string, string> = new Map();
mapping.set("time.Time", "time");
mapping.set("*,time.Time", "time");

this.forEachClassProperty(c, "none", (_name, _jsonName, p) => {
const goType = this.propertyGoType(p);
usedTypes.add(goType.toString());
});

const imports = new Set<string>();
usedTypes.forEach(k => {
const typeImport = mapping.get(k);
if (typeImport) {
imports.add(typeImport);
}
});

return imports;
}

private collectUnionImports(u: UnionType): Set<string> {
const usedTypes = new Set<string>();
const mapping: Map<string, string> = new Map();
mapping.set("time.Time", "time");
mapping.set("*,time.Time", "time");

this.forEachUnionMember(u, null, "none", null, (_fieldName, t) => {
const goType = this.nullableGoType(t, true);
usedTypes.add(goType.toString());
});

const imports = new Set<string>();
usedTypes.forEach(k => {
const typeImport = mapping.get(k);
if (!typeImport) {
return;
}

imports.add(typeImport);
});

return imports;
}
}
Loading

0 comments on commit 1f89a97

Please sign in to comment.