1
0
Fork 0
mirror of https://github.com/HenkKalkwater/harbour-sailfin.git synced 2025-04-06 20:52: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 { enum Value {
EnumNotSet, EnumNotSet,
{{#each values as |value|}} {{#each entries as |entry|}}
{{value}}, {{entry.name}},
{{/each}} {{/each}}
}; };

View file

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

View file

@ -1,6 +1,6 @@
#!/usr/bin/env dub #!/usr/bin/env dub
/+ dub.sdl: /+ dub.sdl:
name "openapigenerator.d" name "openapigenerator"
dependency "dyaml" version="~>0.8.0" dependency "dyaml" version="~>0.8.0"
dependency "handlebars" version="~>0.2.2" dependency "handlebars" version="~>0.2.2"
stringImportPaths "codegen" stringImportPaths "codegen"
@ -115,6 +115,7 @@ string[string] memberAliases;
static this() { static this() {
memberAliases["id"] = "jellyfinId"; memberAliases["id"] = "jellyfinId";
memberAliases["static"] = "staticStreaming"; memberAliases["static"] = "staticStreaming";
memberAliases["auto"] = "automatic";
} }
CasePolicy OPENAPI_CASING = CasePolicy.PASCAL; 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[3] imports = ["QJsonValue", "QObject", "QString"];
string[1] userImports = [buildPath(SUPPORT_FOLDER, "jsonconv.h")]; string[1] userImports = [buildPath(SUPPORT_FOLDER, "jsonconv.h")];
Appender!(string[]) values; Appender!(EnumEntry[]) entries;
foreach (string value; scheme["enum"]) { 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); writeHeaderPreamble(headerFile, CPP_NAMESPACE_DTO, name, imports, userImports);
writeEnumHeader(headerFile, name, values[]); writeEnumHeader(headerFile, name, entries[]);
writeHeaderPostamble(headerFile, CPP_NAMESPACE_DTO, name); writeHeaderPostamble(headerFile, CPP_NAMESPACE_DTO, name);
writeImplementationPreamble(implementationFile, CPP_NAMESPACE_DTO, MODEL_FOLDER, name); writeImplementationPreamble(implementationFile, CPP_NAMESPACE_DTO, MODEL_FOLDER, name);
writeEnumImplementation(implementationFile, name, values[]); writeEnumImplementation(implementationFile, name, entries[]);
writeImplementationPostamble(implementationFile, CPP_NAMESPACE_DTO, name); 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. * parameter could refrence.
*/ */
MetaTypeInfo getType(ref const string name, const ref Node node, const ref Node allSchemas) { MetaTypeInfo getType(ref const string name, const ref Node node, const ref Node allSchemas) {
MetaTypeInfo[] allOf = [];
MetaTypeInfo info = new MetaTypeInfo(); MetaTypeInfo info = new MetaTypeInfo();
info.originalName = name; info.originalName = name;
info.name = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_MEMBER_CASING); 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", ""); info.defaultValue = node.getOr!string("default", "");
if ("description" in node) { 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"; info.typeNullableSetter = "= " ~ info.typeName ~ "::EnumNotSet";
} else if ("type" in allSchemas[type] } else if ("type" in allSchemas[type]
&& allSchemas[type]["type"].as!string == "object") { && allSchemas[type]["type"].as!string == "object") {
writefln("Type %s is an object", type);
info.needsPointer = true; info.needsPointer = true;
info.isTypeNullable = true; info.isTypeNullable = true;
info.typeNullableCheck = ".isNull()"; info.typeNullableCheck = ".isNull()";
@ -714,7 +739,7 @@ MetaTypeInfo getType(ref const string name, const ref Node node, const ref Node
} }
// Type is an enumeration // Type is an enumeration
if ("enum" in node) { if ("enum" in node && !("type" in node)) {
info.isTypeNullable = true; info.isTypeNullable = true;
info.typeNullableCheck = "== " ~ info.typeName ~ "::EnumNotSet"; info.typeNullableCheck = "== " ~ info.typeName ~ "::EnumNotSet";
info.typeNullableSetter = "= " ~ info.typeName ~ "::EnumNotSet"; info.typeNullableSetter = "= " ~ info.typeName ~ "::EnumNotSet";
@ -857,27 +882,27 @@ void writeObjectImplementation(File output, string name, MetaTypeInfo[] properti
} }
// Enum // Enum
void writeEnumHeader(File output, string name, string[] values, string doc = "") { void writeEnumHeader(File output, string name, EnumEntry[] entries, string doc = "") {
class Controller { class Controller {
string className; string className;
string[] values; EnumEntry[] entries;
string supportNamespace = namespaceString!CPP_NAMESPACE_SUPPORT; string supportNamespace = namespaceString!CPP_NAMESPACE_SUPPORT;
} }
Controller controller = new Controller(); Controller controller = new Controller();
controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING);
controller.values = values; controller.entries = entries;
output.writeln(render!(import("enum_header.hbs"), Controller)(controller)); 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 { class Controller {
string className; string className;
string[] values; EnumEntry[] entries;
string supportNamespace = namespaceString!CPP_NAMESPACE_SUPPORT; string supportNamespace = namespaceString!CPP_NAMESPACE_SUPPORT;
} }
Controller controller = new Controller(); Controller controller = new Controller();
controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING);
controller.values = values; controller.entries = entries;
output.writeln(render!(import("enum_implementation.hbs"), Controller)(controller)); output.writeln(render!(import("enum_implementation.hbs"), Controller)(controller));
} }
@ -1147,6 +1172,11 @@ class RequestParameter {
string mimeType; string mimeType;
} }
struct EnumEntry {
string name;
string value;
}
/** /**
* Generates a guard name based on a namespace and class name. * Generates a guard name based on a namespace and class name.
* *