diff --git a/core/codegen/enum_header.hbs b/core/codegen/enum_header.hbs new file mode 100644 index 0000000..e468093 --- /dev/null +++ b/core/codegen/enum_header.hbs @@ -0,0 +1,34 @@ +class {{className}}Class { + Q_GADGET +public: + enum Value { + EnumNotSet, + + {{#each values as |value|}} + {{value}}, + + {{/each}} + }; + Q_ENUM(Value) +private: + explicit {{className}}Class(); +}; + +typedef {{className}} Class::Value {{className}}; + +template <> + +{{className}} fromJsonValue<{{className}}>(QJsonValue source) { + if (!source.isString()) return {{className}}Class::EnumNotSet; + + QString str = source.toString(); + + {{#each values as |value|}} + if (str == QStringLiteral("{{value}}")) { + return {{className}}Class::{{value}}; + } + + {{/each}} + + return {{className}}Class::EnumNotSet; +} diff --git a/core/codegen/object_header.hbs b/core/codegen/object_header.hbs new file mode 100644 index 0000000..b5b3db0 --- /dev/null +++ b/core/codegen/object_header.hbs @@ -0,0 +1,57 @@ +{{#each userImports as |userClass|}} +{{#if userClass != className}} +class {{className}}; + +{{/if}} +{{/each}} + +class {{className}} : public QObject { + Q_OBJECT +public: + explicit {{className}}(QObject *parent = nullptr); + static {{className}} *fromJSON(QJsonObject source, QObject *parent = nullptr); + void updateFromJSON(QJsonObject source, bool emitSignals = true); + QJsonObject toJSON(); + + // Properties + +{{#each properties as |p|}} +{{#if p.description.length > 0}} + /** + * @brief {{p.description}} + + */ +{{/if}} + + Q_PROPERTY({{p.typeNameWithQualifiers}} {{p.name}} READ {{p.name}} WRITE set{{p.writeName}} NOTIFY {{p.name}}Changed) + +{{/each}} + + // Getters + + {{#each properties as |p|}} + {{p.typeNameWithQualifiers}} {{p.name}}() const; + void set{{p.writeName}}({{p.typeNameWithQualifiers}} new{{p.writeName}}); + + + {{/each}} + + // Signals +signals: + + {{#each properties as |p|}} + void {{p.name}}Changed({{p.typeNameWithQualifiers}} new{{p.writeName}}); + + {{/each}} + +protected: + +{{#each properties as |p|}} +{{#if p.defaultInitializer.length > 0}} + {{p.typeNameWithQualifiers}} {{p.memberName}} = {{p.defaultInitializer}}; +{{else}} + {{p.typeNameWithQualifiers}} {{p.memberName}}; +{{/if}} + +{{/each}} +}; diff --git a/core/codegen/object_implementation.hbs b/core/codegen/object_implementation.hbs new file mode 100644 index 0000000..770b707 --- /dev/null +++ b/core/codegen/object_implementation.hbs @@ -0,0 +1,49 @@ +{{className}}::{{className}}(QObject *parent) : QObject(parent) {} + + +{{className}} *{{className}}::fromJSON(QJsonObject source, QObject *parent) { + {{className}} *instance = new {{className}}(parent); + instance->updateFromJSON(source, false); + return instance; +} + + +void {{className}}::updateFromJSON(QJsonObject source, bool emitSignals) { + + {{#each properties as |property|}} + {{property.memberName}} = fromJsonValue<{{property.typeNameWithQualifiers}}>(source["{{property.originalName}}"]); + + {{/each}} + + if (emitSignals) { + + {{#each properties as |property|}} + emit {{property.name}}Changed({{property.memberName}}); + + {{/each}} + } +} + +QJsonObject {{className}}::toJSON() { + QJsonObject result; + + {{#each properties as |property|}} + result["{{property.originalName}}"] = toJsonValue<{{property.typeNameWithQualifiers}}>({{property.memberName}}); + + {{/each}} + + return result; +}" + + +{{#each properties as |property|}} +{{property.typeNameWithQualifiers}} {{className}}::{{property.name}}() const { return {{property.memberName}}; } + +void {{className}}::set{{property.writeName}}({{property.typeNameWithQualifiers}} new{{property.writeName}}) { + + {{property.memberName}} = new{{property.writeName}}; + emit {{property.name}}Changed(new{{property.writeName}}); +} + + +{{/each}} diff --git a/core/openapigenerator.d b/core/openapigenerator.d index 296174d..733c126 100755 --- a/core/openapigenerator.d +++ b/core/openapigenerator.d @@ -2,6 +2,8 @@ /+ dub.sdl: name "openapigenerator.d" dependency "dyaml" version="~>0.8.0" + dependency "handlebars" version="~>0.2.2" + stringImportPaths "codegen" +/ // The following copyright string also applies to this file. @@ -36,6 +38,7 @@ import std.stdio; import std.uni; import dyaml; +import handlebars.tpl; static this() { COPYRIGHT ~= q"EOS @@ -341,156 +344,46 @@ MetaTypeInfo[] collectTypeInfo(Node properties, Node allSchemas) { } void writeObjectHeader(File output, string name, MetaTypeInfo[] properties, string[] userImports) { - string className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); - foreach (userClass; userImports) { - if (userClass != className) output.writefln("class %s;", userClass); + class Controller { + string className; + MetaTypeInfo[] properties; + string[] userImports; } - if (userImports.length > 0) output.writeln(); - output.writefln("class %s : public QObject {", className); - output.writefln("\tQ_OBJECT"); - output.writefln("public:"); - output.writefln("\texplicit %s(QObject *parent = nullptr);", className); - output.writefln("\tstatic %s *fromJSON(QJsonObject source, QObject *parent = nullptr);", className); - output.writefln("\tvoid updateFromJSON(QJsonObject source, bool emitSignals = true);"); - output.writefln("\tQJsonObject toJSON();"); - output.writeln(); + Controller controller = new Controller(); + controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); + controller.properties = properties; + controller.userImports = userImports; - // Generate Q_PROPERTIES - foreach (property; properties) { - // Avoid clashes with QML - if (property.description.length > 0) { - output.writefln("\t/**"); - output.writefln("\t * @brief %s", property.description); - output.writefln("\t */"); - } - output.writefln("\tQ_PROPERTY(%s %s READ %s WRITE set%s NOTIFY %sChanged)", - property.typeNameWithQualifiers, property.name, property.name, property.writeName, property.name); - } + output.writeln(render!(import("object_header.hbs"), Controller)(controller)); - output.writeln(); - // Write getters - foreach (property; properties) { - // Avoid clashes with QML - output.writefln("\t%s %s() const;", - property.typeNameWithQualifiers, property.name); - output.writefln("\tvoid set%s(%s new%s);", - property.writeName, property.typeNameWithQualifiers, property.writeName); - output.writefln("\t"); - } - // Signals - output.writefln("signals:"); - foreach (property; properties) { - output.writefln("\tvoid %sChanged(%s new%s);", - property.name, property.typeNameWithQualifiers, property.writeName); - } - output.writefln("protected:"); - - // Write member variables - foreach (property; properties) { - if (property.defaultInitializer.length > 0) { - output.writefln("\t%s %s = %s;", - property.typeNameWithQualifiers, property.memberName, property.defaultInitializer); - } else { - output.writefln("\t%s %s;", - property.typeNameWithQualifiers, property.memberName); - } - } - - output.writefln("};"); } void writeObjectImplementation(File output, string name, MetaTypeInfo[] properties) { - string className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); - - output.writefln("%s::%s(QObject *parent) : QObject(parent) {}", className, className); - output.writeln(); - - output.writefln("%s *%s::fromJSON(QJsonObject source, QObject *parent) {", className, className); - output.writefln("\t%s *instance = new %s(parent);", className, className); - output.writefln("\tinstance->updateFromJSON(source, false);", className); - output.writefln("\treturn instance;"); - output.writefln("}"); - output.writeln(); - - output.writefln("void %s::updateFromJSON(QJsonObject source, bool emitSignals) {", className, className); - output.writefln("\tQ_UNIMPLEMENTED();"); - foreach (property; properties) { - output.writefln("\t%s = fromJsonValue<%s>(source[\"%s\");", property.memberName, property.typeNameWithQualifiers, - property.originalName); - } - output.writeln(); - output.writefln("\tif (emitSignals) {"); - foreach (property; properties) { - output.writefln("\t\temit %sChanged(%s);", property.name, property.memberName); - } - output.writefln("\t}"); - output.writefln("}"); - - output.writefln("QJsonObject %s::toJSON() {", className); - output.writefln("\tQJsonObject result;"); - foreach (property; properties) { - output.writefln("\tresult[\"%s\"] = toJsonValue<%s>(%s);", property.originalName, property.typeNameWithQualifiers, - property.memberName); - } - output.writefln("\treturn result;"); - output.writefln("}"); - - foreach(property; properties) { - output.writefln("%s %s::%s() const { return %s; }", property.typeNameWithQualifiers, - className, property.name, property.memberName); - - output.writefln("void %s::set%s(%s new%s) {", className, property.writeName, - property.typeNameWithQualifiers, property.writeName); - output.writefln("\t%s = new%s;", property.memberName, property.writeName); - output.writefln("\temit %sChanged(new%s);", property.name, property.writeName); - output.writefln("}"); - output.writeln(); + class Controller { + string className; + MetaTypeInfo[] properties; } + Controller controller = new Controller(); + controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); + controller.properties = properties; + output.writeln(render!(import("object_implementation.hbs"), Controller)(controller)); } // Enum void writeEnumHeader(File output, string name, string[] values, string doc = "") { - string className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); - output.writefln("class %sClass {", className); - output.writefln("\tQ_GADGET"); - output.writefln("public:"); - output.writefln("\tenum Value {"); - output.writefln("\t\tEnumNotSet,"); - foreach (value; values) { - output.writefln("\t\t%s,", value); + class Controller { + string className; + string[] values; } - output.writefln("\t};"); - output.writefln("\tQ_ENUM(Value)"); - output.writefln("private:"); - output.writefln("\texplicit %sClass();", className); - output.writefln("};"); - output.writefln("typedef %sClass::Value %s;", className, className); - output.writeln(); - output.writefln("template <>"); - output.writefln("%s fromJsonValue<%s>(QJsonValue source) {", className, className); - output.writefln("\tif (!source.isString()) return %sClass::EnumNotSet;", className); - output.writeln(); - output.writefln("\tQString str = source.toString();"); - if (values.length > 0) { - output.writefln("\tif (str == QStringLiteral(\"%s\")) {", values[0]); - output.writefln("\t\treturn %sClass::%s;", className, values[0]); - output.write("\t}"); - foreach(value; drop(values, 1)) { - output.writefln(" else if (str == QStringLiteral(\"%s\")) {", value); - output.writefln("\t\treturn %sClass::%s;", className, value); - output.write("\t}"); - } - output.writeln(); - } - output.writeln(); - output.writefln("\treturn %sClass::EnumNotSet;", className); - output.writefln("}"); + Controller controller = new Controller(); + controller.className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); + controller.values = values; + output.writeln(render!(import("enum_header.hbs"), Controller)(controller)); } void writeEnumImplementation(File output, string name) { string className = name.applyCasePolicy(OPENAPI_CASING, CPP_CLASS_CASING); - string importName = name.applyCasePolicy(OPENAPI_CASING, CPP_FILENAME_CASING); output.writefln("%sClass::%sClass() {}", name, name); }