1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-04-03 19:32:41 +00:00

[1/3] update openapi spec: update generator

Update the generator to be able to handle the new challenges that arose
with the new specification, namely:

- Enums that do not start with a capital letter
- Enums that contain a reserved keyword
- Basic handling 'allOf' keys in properties

Additionally, the comparison enum deserialisation is now
case-insensitive.
This commit is contained in:
Chris Josten 2025-03-13 02:34:23 +01:00 committed by Chris Josten
parent 0180aeaab1
commit f71c7a991b
3 changed files with 51 additions and 21 deletions

View file

@ -4,8 +4,8 @@ public:
enum Value {
EnumNotSet,
{{#each values as |value|}}
{{value}},
{{#each entries as |entry|}}
{{entry.name}},
{{/each}}
};

View file

@ -13,9 +13,9 @@ template <>
QString str = source.toString();
{{#each values as |value|}}
if (str == QStringLiteral("{{value}}")) {
return {{className}}::{{value}};
{{#each entries as |entry|}}
if (str == QStringLiteral("{{entry.value}}")) {
return {{className}}::{{entry.name}};
}
{{/each}}
@ -27,9 +27,9 @@ template <>
QJsonValue toJsonValue(const {{className}} &source, convertType<{{className}}>) {
switch(source) {
{{#each values as |value|}}
case {{className}}::{{value}}:
return QStringLiteral("{{value}}");
{{#each entries as |entry|}}
case {{className}}::{{entry.name}}:
return QStringLiteral("{{entry.value}}");
{{/each}}

View file

@ -1,6 +1,6 @@
#!/usr/bin/env dub
/+ dub.sdl:
name "openapigenerator.d"
name "openapigenerator"
dependency "dyaml" version="~>0.8.0"
dependency "handlebars" version="~>0.2.2"
stringImportPaths "codegen"
@ -115,6 +115,7 @@ string[string] memberAliases;
static this() {
memberAliases["id"] = "jellyfinId";
memberAliases["static"] = "staticStreaming";
memberAliases["auto"] = "automatic";
}
CasePolicy OPENAPI_CASING = CasePolicy.PASCAL;
@ -577,17 +578,25 @@ void generateFileForSchema(ref const string name, ref const Node scheme, Node al
string[3] imports = ["QJsonValue", "QObject", "QString"];
string[1] userImports = [buildPath(SUPPORT_FOLDER, "jsonconv.h")];
Appender!(string[]) values;
Appender!(EnumEntry[]) entries;
foreach (string value; scheme["enum"]) {
values ~= value;
EnumEntry entry = { value, value };
if (string* correctedName = value in memberAliases) {
entry.name = *correctedName;
}
if (entry.name[0].isLower()) {
// QML does not like enumeration starting with a lower case letter
entry.name = ([entry.name[0].toUpper()] ~ entry.name[1..$].to!(dchar[])).to!string;
}
entries ~= entry;
}
writeHeaderPreamble(headerFile, CPP_NAMESPACE_DTO, name, imports, userImports);
writeEnumHeader(headerFile, name, values[]);
writeEnumHeader(headerFile, name, entries[]);
writeHeaderPostamble(headerFile, CPP_NAMESPACE_DTO, name);
writeImplementationPreamble(implementationFile, CPP_NAMESPACE_DTO, MODEL_FOLDER, name);
writeEnumImplementation(implementationFile, name, values[]);
writeEnumImplementation(implementationFile, name, entries[]);
writeImplementationPostamble(implementationFile, CPP_NAMESPACE_DTO, name);
}
@ -679,9 +688,26 @@ void generateFileForSchema(ref const string name, ref const Node scheme, Node al
* parameter could refrence.
*/
MetaTypeInfo getType(ref const string name, const ref Node node, const ref Node allSchemas) {
MetaTypeInfo[] allOf = [];
MetaTypeInfo info = new MetaTypeInfo();
info.originalName = name;
info.name = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_MEMBER_CASING);
if (const Node* allOfNodes = "allOf" in node) {
writeln(name, " contains allOf:");
allOf.reserve(node.length);
foreach (const ref Node anotherType; *allOfNodes) {
allOf ~= getType(name, anotherType, allSchemas);
writeln(" - ", allOf[$ - 1].name);
}
}
if (allOf.length == 1 && allOf[0].name == info.name) {
// If it contains a single allOf value, just replace our current type with it.
return allOf[0];
}
info.defaultValue = node.getOr!string("default", "");
if ("description" in node) {
@ -703,7 +729,6 @@ MetaTypeInfo getType(ref const string name, const ref Node node, const ref Node
info.typeNullableSetter = "= " ~ info.typeName ~ "::EnumNotSet";
} else if ("type" in allSchemas[type]
&& allSchemas[type]["type"].as!string == "object") {
writefln("Type %s is an object", type);
info.needsPointer = true;
info.isTypeNullable = true;
info.typeNullableCheck = ".isNull()";
@ -714,7 +739,7 @@ MetaTypeInfo getType(ref const string name, const ref Node node, const ref Node
}
// Type is an enumeration
if ("enum" in node) {
if ("enum" in node && !("type" in node)) {
info.isTypeNullable = true;
info.typeNullableCheck = "== " ~ info.typeName ~ "::EnumNotSet";
info.typeNullableSetter = "= " ~ info.typeName ~ "::EnumNotSet";
@ -857,27 +882,27 @@ void writeObjectImplementation(File output, string name, MetaTypeInfo[] properti
}
// Enum
void writeEnumHeader(File output, string name, string[] values, string doc = "") {
void writeEnumHeader(File output, string name, EnumEntry[] entries, string doc = "") {
class Controller {
string className;
string[] values;
EnumEntry[] entries;
string supportNamespace = namespaceString!CPP_NAMESPACE_SUPPORT;
}
Controller controller = new Controller();
controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING);
controller.values = values;
controller.entries = entries;
output.writeln(render!(import("enum_header.hbs"), Controller)(controller));
}
void writeEnumImplementation(File output, string name, string[] values) {
void writeEnumImplementation(File output, string name, EnumEntry[] entries) {
class Controller {
string className;
string[] values;
EnumEntry[] entries;
string supportNamespace = namespaceString!CPP_NAMESPACE_SUPPORT;
}
Controller controller = new Controller();
controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING);
controller.values = values;
controller.entries = entries;
output.writeln(render!(import("enum_implementation.hbs"), Controller)(controller));
}
@ -1147,6 +1172,11 @@ class RequestParameter {
string mimeType;
}
struct EnumEntry {
string name;
string value;
}
/**
* Generates a guard name based on a namespace and class name.
*