From 51d27f3bcaacefda78dc033021b6a85152ab972e Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 1 Jun 2020 14:53:59 +0200 Subject: GTAD parts: new home and format updates for GTAD 0.7 All GTAD-related files (gtad.yaml and templates) from now live in their dedicated gtad/ directory - this helps against removing them accidentally along with the rest of the generated files. The format to list generated files in gtad.yaml has changed a bit before GTAD 0.7 beta2; gtad.yaml in this commit conforms to the new structure. --- CMakeLists.txt | 11 ++- gtad/gtad.yaml | 169 +++++++++++++++++++++++++++++++++++++ gtad/preamble.mustache | 3 + gtad/template.cpp.mustache | 182 ++++++++++++++++++++++++++++++++++++++++ gtad/template.h.mustache | 119 ++++++++++++++++++++++++++ lib/csapi/gtad.yaml | 165 ------------------------------------ lib/csapi/preamble.mustache | 3 - lib/csapi/{{base}}.cpp.mustache | 182 ---------------------------------------- lib/csapi/{{base}}.h.mustache | 119 -------------------------- 9 files changed, 478 insertions(+), 475 deletions(-) create mode 100644 gtad/gtad.yaml create mode 100644 gtad/preamble.mustache create mode 100644 gtad/template.cpp.mustache create mode 100644 gtad/template.h.mustache delete mode 100644 lib/csapi/gtad.yaml delete mode 100644 lib/csapi/preamble.mustache delete mode 100644 lib/csapi/{{base}}.cpp.mustache delete mode 100644 lib/csapi/{{base}}.h.mustache diff --git a/CMakeLists.txt b/CMakeLists.txt index 68e49a67..1e336673 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -200,7 +200,6 @@ endforeach() # new files you have to re-run cmake. # TODO: check `file(GLOB_RECURSE ... CONFIGURE_DEPENDS)` (from CMake 3.14) if (MATRIX_DOC_PATH AND GTAD_PATH) - set(FULL_CSAPI_DIR lib/${CSAPI_DIR}) set(FULL_CSAPI_SRC_DIR ${ABS_API_DEF_PATH}/client-server) file(GLOB_RECURSE API_DEFS RELATIVE ${PROJECT_SOURCE_DIR} ${FULL_CSAPI_SRC_DIR}/*.yaml @@ -208,15 +207,15 @@ if (MATRIX_DOC_PATH AND GTAD_PATH) ${ABS_API_DEF_PATH}/${ISAPI_DEF_DIR}/*.yaml ) add_custom_target(update-api - ${ABS_GTAD_PATH} --config ${CSAPI_DIR}/gtad.yaml --out ${CSAPI_DIR} + ${ABS_GTAD_PATH} --config ../gtad/gtad.yaml --out ${CSAPI_DIR} ${FULL_CSAPI_SRC_DIR} old_sync.yaml- room_initial_sync.yaml- # deprecated search.yaml- # current GTAD is limited in handling move-only data sync.yaml- # we have a better handcrafted implementation WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/lib - SOURCES ${FULL_CSAPI_DIR}/gtad.yaml - ${FULL_CSAPI_DIR}/{{base}}.h.mustache - ${FULL_CSAPI_DIR}/{{base}}.cpp.mustache + SOURCES gtad/gtad.yaml + gtad/template.h.mustache + gtad/template.cpp.mustache ${API_DEFS} VERBATIM ) @@ -232,7 +231,7 @@ if (MATRIX_DOC_PATH AND GTAD_PATH) COMMAND ${ABS_CLANG_FORMAT} ${CLANG_FORMAT_ARGS} ${api_HDRS} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} VERBATIM - COMMENT Formatting files + COMMENT "Formatting files" ) endif() endif() diff --git a/gtad/gtad.yaml b/gtad/gtad.yaml new file mode 100644 index 00000000..51f9e26b --- /dev/null +++ b/gtad/gtad.yaml @@ -0,0 +1,169 @@ +analyzer: + subst: + "%CLIENT_RELEASE_LABEL%": r0 + "%CLIENT_MAJOR_VERSION%": r0 + identifiers: + signed: signedData + unsigned: unsignedData + PushRule/default: isDefault + default: defaultVersion # getCapabilities/RoomVersionsCapability + origin_server_ts: originServerTimestamp # Instead of originServerTs + start: begin # Because start() is a method in BaseJob + m.upload.size: uploadSize + m.homeserver: homeserver + m.identity_server: identityServer + m.change_password: changePassword + m.room_versions: roomVersions + AuthenticationData/additionalProperties: authInfo + + # Structure inside `types`: + # - swaggerType: + # OR + # - swaggerType: + # - swaggerFormat: + # - /swaggerFormatRegEx/: + # - //: # default, if the format doesn't mach anything above + # WHERE + # targetTypeSpec = targetType OR + # { type: targetType, imports: , } + # swaggerType can be +set/+on pair; attributes from the map under +set + # are added to each type from the sequence under +on. + types: + - +set: &UseOmittable + useOmittable: + imports: [ '"converters.h"' ] + omittedValue: 'none' # See `none` in converters.h + +on: + - integer: + - int64: qint64 + - int32: qint32 + - //: int + - number: + - float: float + - //: double + - boolean: bool + - string: + - byte: &ByteStream + type: QIODevice* + imports: + - binary: *ByteStream + - +set: { avoidCopy: } + +on: + - date: + type: QDate + initializer: QDate::fromString("{{defaultValue}}") + imports: + - dateTime: + type: QDateTime + initializer: QDateTime::fromString("{{defaultValue}}") + imports: + - //: &QString + type: QString + initializer: QStringLiteral("{{defaultValue}}") + isString: + - file: *ByteStream + - +set: { avoidCopy: } + +on: + - object: &QJsonObject { type: QJsonObject, imports: } + - $ref: + - +set: { moveOnly: } + +on: + - /state_event.yaml$/: + { type: StateEventPtr, imports: '"events/eventloader.h"' } + - /room_event.yaml$/: + { type: RoomEventPtr, imports: '"events/eventloader.h"' } + - /event.yaml$/: + { type: EventPtr, imports: '"events/eventloader.h"' } + - /m\.room\.member$/: pass # This $ref is only used in an array, see below + - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types + - schema: # Properties of inline structure definitions + - TurnServerCredentials: *QJsonObject # Because it's used as is + - //: *UseOmittable + - array: + - string: QStringList + - +set: { moveOnly: } + +on: + - /^Notification|Result$/: + type: "std::vector<{{1}}>" + imports: '"events/eventloader.h"' + - /m\.room\.member$/: + type: "EventsArray" + imports: '"events/roommemberevent.h"' + - /state_event.yaml$/: StateEvents + - /room_event.yaml$/: RoomEvents + - /event.yaml$/: Events + - //: { type: "QVector<{{1}}>", imports: } + - map: # `additionalProperties` in OpenAPI + - RoomState: + type: "UnorderedMap" + moveOnly: + imports: '"util.h"' + - /.+/: + type: "QHash" + imports: + - //: + type: QVariantHash + imports: + - variant: # A sequence `type` (multitype) in OpenAPI + - /^string,null|null,string$/: *QString + - //: { type: QVariant, imports: } + + #operations: + +mustache: + constants: + # Syntax elements used by GTAD +# _quote: '"' # Common quote for left and right +# _leftQuote: '"' +# _rightQuote: '"' +# _joinChar: ',' # The character used by {{_join}} - not working yet + _comment: '//' + copyrightName: Kitsune Ral + copyrightEmail: + + partials: + _typeRenderer: "{{#scope}}{{scopeCamelCase}}Job::{{/scope}}{{>name}}" + omittedValue: '{}' # default value to initialize omitted parameters with + initializer: '{{defaultValue}}' + cjoin: '{{#hasMore}}, {{/hasMore}}' + + openOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}Omittable<{{/defaultValue}}{{/useOmittable}}{{/required?}}" + closeOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}>{{/defaultValue}}{{/useOmittable}}{{/required?}}" + + maybeOmittableType: "{{>openOmittable}}{{dataType.name}}{{>closeOmittable}}" + qualifiedMaybeOmittableType: "{{>openOmittable}}{{dataType.qualifiedName}}{{>closeOmittable}}" + + maybeCrefType: "{{#avoidCopy}}const {{/avoidCopy}}{{>maybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + qualifiedMaybeCrefType: + "{{#avoidCopy}}const {{/avoidCopy}}{{>qualifiedMaybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" + + maybeCrefJsonObject: "{{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}}" + takeOrValue: "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" + + initializeDefaultValue: "{{#defaultValue}}{{>initializer}}{{/defaultValue}}{{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" + joinedParamDecl: '{{>maybeCrefType}} {{paramName}}{{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}}' + joinedParamDef: '{{>maybeCrefType}} {{paramName}}{{>cjoin}}' + passQueryParams: '{{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}' + + # Doc-comment blocks. Comment indent is managed by clang-format + # (without clang-format there'd have to be a separate partial definition + # for each indent...) + + # For structures that are not supposed to have a summary (e.g., JSON schema) + docCommentShort: |- + {{#description}} + /// {{_}}{{/description}} + docCommentSummary: |- + {{#summary}} \brief {{summary}} + *{{/summary}} + + templates: + data: + .h: "{{>template.h.mustache}}" + .cpp: "{{>template.cpp.mustache}}" + api: + .h: "{{>template.h.mustache}}" + .cpp: "{{>template.cpp.mustache}}" + + #outFilesList: apifiles.txt + diff --git a/gtad/preamble.mustache b/gtad/preamble.mustache new file mode 100644 index 00000000..3ba87d61 --- /dev/null +++ b/gtad/preamble.mustache @@ -0,0 +1,3 @@ +/****************************************************************************** + * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN + */ diff --git a/gtad/template.cpp.mustache b/gtad/template.cpp.mustache new file mode 100644 index 00000000..b3bd4de9 --- /dev/null +++ b/gtad/template.cpp.mustache @@ -0,0 +1,182 @@ +{{>preamble}} +#include "{{filenameBase}}.h" +{{^models}} +#include "converters.h"{{/models}} +{{#operations}} + {{#producesNonJson?}} +#include + {{/producesNonJson?}} +#include +{{/operations}} + +using namespace Quotient; + +{{#models.model}} + {{#in?}} +void JsonObjectConverter<{{qualifiedName}}>::dumpTo( + QJsonObject& jo, const {{qualifiedName}}& pod) +{ {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} +} + {{/in?}} + + {{#out?}} +void JsonObjectConverter<{{qualifiedName}}>::fillFrom( + {{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) +{ {{#parents}} + fillFromJson<{{qualifiedName}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} +} + {{/out?}} + +{{/models.model}} +{{#operations}} + +static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); + {{#operation}}{{#models}} + +// Converters +namespace Quotient { + {{#model}} + +template <> struct JsonObjectConverter<{{qualifiedName}}> { + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) + { {{#propertyMap}} + fillJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}}{{#parents}} + fillJson<{{name}}>(jo, pod); + {{/parents}}{{#vars}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, + QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); + {{/vars}} + } + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) + { {{#parents}} + fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), + result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} + } + {{/out?}} +}; + {{/model}} + +} // namespace Quotient + {{/models}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + +class {{camelCaseOperationId}}Job::Private +{ + public:{{#allProperties}} + {{>maybeOmittableType}} {{paramName}};{{/allProperties}} +}; + {{/allProperties?}}{{/normalResponse?}}{{/responses}} + {{#queryParams?}} + +BaseJob::Query queryTo{{camelCaseOperationId}}( + {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) +{ + BaseJob::Query _q;{{#queryParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, + QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} + return _q; +} + {{/queryParams?}} + {{^bodyParams}} + +QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) +{ + return BaseJob::makeRequestUrl(std::move(baseUrl), + basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, + queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}); +} {{/bodyParams}} + +{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( + {{#allParams}}{{>joinedParamDef}}{{/allParams}}) + : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, + QStringLiteral("{{camelCaseOperationId}}Job"), {{!object name}} + basePath{{#pathParts}} % {{_}}{{/pathParts}} {{!API endpoint}} + {{#queryParams? + }} , queryTo{{camelCaseOperationId}}({{>passQueryParams}}) + {{/queryParams? + }}{{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) + {{#responses}}{{#normalResponse?}}{{#allProperties? + }}, d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} +{ {{#headerParams}} + setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); + {{/headerParams}}{{#bodyParams?}} + {{#inlineBody}} + setRequestData(Data({{!avoid extra linebreaks}}{{ + #consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson?}}{{ + ^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson? + }})); + {{/inlineBody}}{{^inlineBody}} + QJsonObject _data; + {{#bodyParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, + QStringLiteral("{{baseName}}"), {{paramName}}); + {{/bodyParams}} + setRequestData(_data); + {{/inlineBody}} + {{/bodyParams?}}{{#producesNonJson?}} + setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); + {{/producesNonJson?}} +} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + +{{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default; + {{#allProperties}} + +{{>qualifiedMaybeCrefType}} + {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} +{ + return {{#moveOnly}}std::move({{/moveOnly}}d->{{paramName}}{{#moveOnly}}){{/moveOnly}}; +} + {{/allProperties}} + + {{#producesNonJson?}} +BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) +{ + {{! We don't check for required headers yet }} + {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}"); + {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}} + return Success; +} + {{/producesNonJson?}}{{^producesNonJson?}} +BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) +{ + {{#inlineResponse}} + fromJson(data, d->{{paramName}}); + {{/inlineResponse}}{{^inlineResponse}} + auto json = data.object(); + {{#properties}}{{#required?}} + if (!json.contains("{{baseName}}"_ls)) + return { IncorrectResponse, + "The key '{{baseName}}' not found in the response" }; + {{/required?}} + fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); + {{/properties}} + {{/inlineResponse}} + + return Success; +} + {{/producesNonJson?}} + {{/allProperties?}}{{/normalResponse?}}{{/responses}} +{{/operation}}{{/operations}} diff --git a/gtad/template.h.mustache b/gtad/template.h.mustache new file mode 100644 index 00000000..404aafe8 --- /dev/null +++ b/gtad/template.h.mustache @@ -0,0 +1,119 @@ +{{>preamble}} +#pragma once + +{{#operations}} +#include "jobs/basejob.h"{{/operations}} +{{#models}} +#include "converters.h"{{/models}} +{{#imports}} +#include {{_}}{{/imports}} + +namespace Quotient { +{{#models}} + +// Data structures + + {{#model}} +{{>docCommentShort}} +struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} +{ {{#vars}} + + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/vars}}{{#propertyMap}} + + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/propertyMap}} +}; + +template <> struct JsonObjectConverter<{{name}}> +{ + {{#in?}} + static void dumpTo(QJsonObject& jo, const {{name}}& pod); + {{/in?}} + {{#out?}} + static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod); + {{/out?}} +}; + {{/model}} +{{/models}} +{{#operations}} + +// Operations + {{#operation}} + +/*!{{>docCommentSummary}}{{#description}} + * {{_}}{{/description}} + */ +class {{camelCaseOperationId}}Job : public BaseJob { +public: {{#models}} + // Inner data structures + {{#model}} + + {{>docCommentShort}} + struct {{name}}{{#parents?}} : + {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} + { + {{#vars}} + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/vars}} + {{#propertyMap}} + {{>docCommentShort}} + {{>maybeOmittableType}} {{nameCamelCase}}; + {{/propertyMap}} + }; + {{/model}} + + // Construction/destruction + + {{/models}} + {{^allParams?}} + {{#summary}} + /// {{summary}} + {{/summary}} + {{/allParams?}}{{#allParams?}} + /*!{{>docCommentSummary}} + {{#allParams}} + * \param {{nameCamelCase}}{{#description}} + * {{_}}{{/description}} + {{/allParams}} + */ + {{/allParams?}} + explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); + {{^bodyParams}} + + /*! \brief Construct a URL without creating a full-fledged job object + * + * This function can be used when a URL for {{camelCaseOperationId}}Job + * is necessary but the job itself isn't. + */ + static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); + {{/bodyParams}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + ~{{camelCaseOperationId}}Job() override; + + // Result properties + {{#allProperties}} + + {{>docCommentShort}} + {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}}; + {{/allProperties}} + +protected: {{#producesNonJson?}} + Status parseReply(QNetworkReply* reply) override; + {{/producesNonJson?}}{{^producesNonJson?}} + Status parseJson(const QJsonDocument& data) override; + {{/producesNonJson?}} + +private: + class Private; + QScopedPointer d; + {{/allProperties?}}{{/normalResponse?}}{{/responses}} +}; + {{/operation}} +{{/operations}} + +} // namespace Quotient diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml deleted file mode 100644 index 086bd4f7..00000000 --- a/lib/csapi/gtad.yaml +++ /dev/null @@ -1,165 +0,0 @@ -analyzer: - subst: - "%CLIENT_RELEASE_LABEL%": r0 - "%CLIENT_MAJOR_VERSION%": r0 - identifiers: - signed: signedData - unsigned: unsignedData - PushRule/default: isDefault - default: defaultVersion # getCapabilities/RoomVersionsCapability - origin_server_ts: originServerTimestamp # Instead of originServerTs - start: begin # Because start() is a method in BaseJob - m.upload.size: uploadSize - m.homeserver: homeserver - m.identity_server: identityServer - m.change_password: changePassword - m.room_versions: roomVersions - AuthenticationData/additionalProperties: authInfo - - # Structure inside `types`: - # - swaggerType: - # OR - # - swaggerType: - # - swaggerFormat: - # - /swaggerFormatRegEx/: - # - //: # default, if the format doesn't mach anything above - # WHERE - # targetTypeSpec = targetType OR - # { type: targetType, imports: , } - # swaggerType can be +set/+on pair; attributes from the map under +set - # are added to each type from the sequence under +on. - types: - - +set: &UseOmittable - useOmittable: - imports: [ '"converters.h"' ] - omittedValue: 'none' # See `none` in converters.h - +on: - - integer: - - int64: qint64 - - int32: qint32 - - //: int - - number: - - float: float - - //: double - - boolean: bool - - string: - - byte: &ByteStream - type: QIODevice* - imports: - - binary: *ByteStream - - +set: { avoidCopy: } - +on: - - date: - type: QDate - initializer: QDate::fromString("{{defaultValue}}") - imports: - - dateTime: - type: QDateTime - initializer: QDateTime::fromString("{{defaultValue}}") - imports: - - //: &QString - type: QString - initializer: QStringLiteral("{{defaultValue}}") - isString: - - file: *ByteStream - - +set: { avoidCopy: } - +on: - - object: &QJsonObject { type: QJsonObject, imports: } - - $ref: - - +set: { moveOnly: } - +on: - - /state_event.yaml$/: - { type: StateEventPtr, imports: '"events/eventloader.h"' } - - /room_event.yaml$/: - { type: RoomEventPtr, imports: '"events/eventloader.h"' } - - /event.yaml$/: - { type: EventPtr, imports: '"events/eventloader.h"' } - - /m\.room\.member$/: pass # This $ref is only used in an array, see below - - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types - - schema: # Properties of inline structure definitions - - TurnServerCredentials: *QJsonObject # Because it's used as is - - //: *UseOmittable - - array: - - string: QStringList - - +set: { moveOnly: } - +on: - - /^Notification|Result$/: - type: "std::vector<{{1}}>" - imports: '"events/eventloader.h"' - - /m\.room\.member$/: - type: "EventsArray" - imports: '"events/roommemberevent.h"' - - /state_event.yaml$/: StateEvents - - /room_event.yaml$/: RoomEvents - - /event.yaml$/: Events - - //: { type: "QVector<{{1}}>", imports: } - - map: # `additionalProperties` in OpenAPI - - RoomState: - type: "UnorderedMap" - moveOnly: - imports: '"util.h"' - - /.+/: - type: "QHash" - imports: - - //: - type: QVariantHash - imports: - - variant: # A sequence `type` (multitype) in OpenAPI - - /^string,null|null,string$/: *QString - - //: { type: QVariant, imports: } - - #operations: - -mustache: - constants: - # Syntax elements used by GTAD -# _quote: '"' # Common quote for left and right -# _leftQuote: '"' -# _rightQuote: '"' -# _joinChar: ',' # The character used by {{_join}} - not working yet - _comment: '//' - copyrightName: Kitsune Ral - copyrightEmail: - - partials: - _typeRenderer: "{{#scope}}{{scopeCamelCase}}Job::{{/scope}}{{>name}}" - omittedValue: '{}' # default value to initialize omitted parameters with - initializer: '{{defaultValue}}' - cjoin: '{{#hasMore}}, {{/hasMore}}' - - openOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}Omittable<{{/defaultValue}}{{/useOmittable}}{{/required?}}" - closeOmittable: "{{^required?}}{{#useOmittable}}{{^defaultValue}}>{{/defaultValue}}{{/useOmittable}}{{/required?}}" - - maybeOmittableType: "{{>openOmittable}}{{dataType.name}}{{>closeOmittable}}" - qualifiedMaybeOmittableType: "{{>openOmittable}}{{dataType.qualifiedName}}{{>closeOmittable}}" - - maybeCrefType: "{{#avoidCopy}}const {{/avoidCopy}}{{>maybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" - qualifiedMaybeCrefType: - "{{#avoidCopy}}const {{/avoidCopy}}{{>qualifiedMaybeOmittableType}}{{#avoidCopy}}&{{/avoidCopy}}{{#moveOnly}}&&{{/moveOnly}}" - - maybeCrefJsonObject: "{{^propertyMap}}const QJsonObject&{{/propertyMap}}{{#propertyMap}}QJsonObject{{/propertyMap}}" - takeOrValue: "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" - - initializeDefaultValue: "{{#defaultValue}}{{>initializer}}{{/defaultValue}}{{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" - joinedParamDecl: '{{>maybeCrefType}} {{paramName}}{{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}}' - joinedParamDef: '{{>maybeCrefType}} {{paramName}}{{>cjoin}}' - passQueryParams: '{{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}' - - # Doc-comment blocks. Comment indent is managed by clang-format - # (without clang-format there'd have to be a separate partial definition - # for each indent...) - - # For structures that are not supposed to have a summary (e.g., JSON schema) - docCommentShort: |- - {{#description}} - /// {{_}}{{/description}} - docCommentSummary: |- - {{#summary}} \brief {{summary}} - *{{/summary}} - - templates: - - "{{base}}.h.mustache" - - "{{base}}.cpp.mustache" - - #outFilesList: apifiles.txt - diff --git a/lib/csapi/preamble.mustache b/lib/csapi/preamble.mustache deleted file mode 100644 index 3ba87d61..00000000 --- a/lib/csapi/preamble.mustache +++ /dev/null @@ -1,3 +0,0 @@ -/****************************************************************************** - * THIS FILE IS GENERATED - ANY EDITS WILL BE OVERWRITTEN - */ diff --git a/lib/csapi/{{base}}.cpp.mustache b/lib/csapi/{{base}}.cpp.mustache deleted file mode 100644 index b3bd4de9..00000000 --- a/lib/csapi/{{base}}.cpp.mustache +++ /dev/null @@ -1,182 +0,0 @@ -{{>preamble}} -#include "{{filenameBase}}.h" -{{^models}} -#include "converters.h"{{/models}} -{{#operations}} - {{#producesNonJson?}} -#include - {{/producesNonJson?}} -#include -{{/operations}} - -using namespace Quotient; - -{{#models.model}} - {{#in?}} -void JsonObjectConverter<{{qualifiedName}}>::dumpTo( - QJsonObject& jo, const {{qualifiedName}}& pod) -{ {{#propertyMap}} - fillJson(jo, pod.{{nameCamelCase}}); - {{/propertyMap}}{{#parents}} - fillJson<{{name}}>(jo, pod); - {{/parents}}{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, - QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); - {{/vars}} -} - {{/in?}} - - {{#out?}} -void JsonObjectConverter<{{qualifiedName}}>::fillFrom( - {{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) -{ {{#parents}} - fillFromJson<{{qualifiedName}}>(jo, result); - {{/parents}}{{#vars}} - fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), result.{{nameCamelCase}}); - {{/vars}}{{#propertyMap}} - fromJson(jo, result.{{nameCamelCase}}); - {{/propertyMap}} -} - {{/out?}} - -{{/models.model}} -{{#operations}} - -static const auto basePath = QStringLiteral("{{basePathWithoutHost}}"); - {{#operation}}{{#models}} - -// Converters -namespace Quotient { - {{#model}} - -template <> struct JsonObjectConverter<{{qualifiedName}}> { - {{#in?}} - static void dumpTo(QJsonObject& jo, const {{qualifiedName}}& pod) - { {{#propertyMap}} - fillJson(jo, pod.{{nameCamelCase}}); - {{/propertyMap}}{{#parents}} - fillJson<{{name}}>(jo, pod); - {{/parents}}{{#vars}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(jo, - QStringLiteral("{{baseName}}"), pod.{{nameCamelCase}}); - {{/vars}} - } - {{/in?}} - {{#out?}} - static void fillFrom({{>maybeCrefJsonObject}} jo, {{qualifiedName}}& result) - { {{#parents}} - fillFromJson<{{qualifiedName}}{{!of the parent!}}>(jo, result); - {{/parents}}{{#vars}} - fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), - result.{{nameCamelCase}}); - {{/vars}}{{#propertyMap}} - fromJson(jo, result.{{nameCamelCase}}); - {{/propertyMap}} - } - {{/out?}} -}; - {{/model}} - -} // namespace Quotient - {{/models}} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - -class {{camelCaseOperationId}}Job::Private -{ - public:{{#allProperties}} - {{>maybeOmittableType}} {{paramName}};{{/allProperties}} -}; - {{/allProperties?}}{{/normalResponse?}}{{/responses}} - {{#queryParams?}} - -BaseJob::Query queryTo{{camelCaseOperationId}}( - {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) -{ - BaseJob::Query _q;{{#queryParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, - QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} - return _q; -} - {{/queryParams?}} - {{^bodyParams}} - -QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, - {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) -{ - return BaseJob::makeRequestUrl(std::move(baseUrl), - basePath{{#pathParts}} % {{_}}{{/pathParts}}{{#queryParams?}}, - queryTo{{camelCaseOperationId}}({{>passQueryParams}}){{/queryParams?}}); -} {{/bodyParams}} - -{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( - {{#allParams}}{{>joinedParamDef}}{{/allParams}}) - : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, - QStringLiteral("{{camelCaseOperationId}}Job"), {{!object name}} - basePath{{#pathParts}} % {{_}}{{/pathParts}} {{!API endpoint}} - {{#queryParams? - }} , queryTo{{camelCaseOperationId}}({{>passQueryParams}}) - {{/queryParams? - }}{{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) - {{#responses}}{{#normalResponse?}}{{#allProperties? - }}, d(new Private){{/allProperties?}}{{/normalResponse?}}{{/responses}} -{ {{#headerParams}} - setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); - {{/headerParams}}{{#bodyParams?}} - {{#inlineBody}} - setRequestData(Data({{!avoid extra linebreaks}}{{ - #consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson?}}{{ - ^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson? - }})); - {{/inlineBody}}{{^inlineBody}} - QJsonObject _data; - {{#bodyParams}} - addParam<{{^required?}}IfNotEmpty{{/required?}}>(_data, - QStringLiteral("{{baseName}}"), {{paramName}}); - {{/bodyParams}} - setRequestData(_data); - {{/inlineBody}} - {{/bodyParams?}}{{#producesNonJson?}} - setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); - {{/producesNonJson?}} -} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - -{{camelCaseOperationId}}Job::~{{camelCaseOperationId}}Job() = default; - {{#allProperties}} - -{{>qualifiedMaybeCrefType}} - {{camelCaseOperationId}}Job::{{paramName}}(){{^moveOnly}} const{{/moveOnly}} -{ - return {{#moveOnly}}std::move({{/moveOnly}}d->{{paramName}}{{#moveOnly}}){{/moveOnly}}; -} - {{/allProperties}} - - {{#producesNonJson?}} -BaseJob::Status {{camelCaseOperationId}}Job::parseReply(QNetworkReply* reply) -{ - {{! We don't check for required headers yet }} - {{#headers}}d->{{paramName}} = reply->rawHeader("{{baseName}}"); - {{/headers}}{{#properties}}d->{{paramName}} = reply;{{/properties}} - return Success; -} - {{/producesNonJson?}}{{^producesNonJson?}} -BaseJob::Status {{camelCaseOperationId}}Job::parseJson(const QJsonDocument& data) -{ - {{#inlineResponse}} - fromJson(data, d->{{paramName}}); - {{/inlineResponse}}{{^inlineResponse}} - auto json = data.object(); - {{#properties}}{{#required?}} - if (!json.contains("{{baseName}}"_ls)) - return { IncorrectResponse, - "The key '{{baseName}}' not found in the response" }; - {{/required?}} - fromJson(json.value("{{baseName}}"_ls), d->{{paramName}}); - {{/properties}} - {{/inlineResponse}} - - return Success; -} - {{/producesNonJson?}} - {{/allProperties?}}{{/normalResponse?}}{{/responses}} -{{/operation}}{{/operations}} diff --git a/lib/csapi/{{base}}.h.mustache b/lib/csapi/{{base}}.h.mustache deleted file mode 100644 index 404aafe8..00000000 --- a/lib/csapi/{{base}}.h.mustache +++ /dev/null @@ -1,119 +0,0 @@ -{{>preamble}} -#pragma once - -{{#operations}} -#include "jobs/basejob.h"{{/operations}} -{{#models}} -#include "converters.h"{{/models}} -{{#imports}} -#include {{_}}{{/imports}} - -namespace Quotient { -{{#models}} - -// Data structures - - {{#model}} -{{>docCommentShort}} -struct {{name}}{{#parents?}} : {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} -{ {{#vars}} - - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/vars}}{{#propertyMap}} - - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/propertyMap}} -}; - -template <> struct JsonObjectConverter<{{name}}> -{ - {{#in?}} - static void dumpTo(QJsonObject& jo, const {{name}}& pod); - {{/in?}} - {{#out?}} - static void fillFrom({{>maybeCrefJsonObject}} jo, {{name}}& pod); - {{/out?}} -}; - {{/model}} -{{/models}} -{{#operations}} - -// Operations - {{#operation}} - -/*!{{>docCommentSummary}}{{#description}} - * {{_}}{{/description}} - */ -class {{camelCaseOperationId}}Job : public BaseJob { -public: {{#models}} - // Inner data structures - {{#model}} - - {{>docCommentShort}} - struct {{name}}{{#parents?}} : - {{#parents}}{{name}}{{>cjoin}}{{/parents}}{{/parents?}} - { - {{#vars}} - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/vars}} - {{#propertyMap}} - {{>docCommentShort}} - {{>maybeOmittableType}} {{nameCamelCase}}; - {{/propertyMap}} - }; - {{/model}} - - // Construction/destruction - - {{/models}} - {{^allParams?}} - {{#summary}} - /// {{summary}} - {{/summary}} - {{/allParams?}}{{#allParams?}} - /*!{{>docCommentSummary}} - {{#allParams}} - * \param {{nameCamelCase}}{{#description}} - * {{_}}{{/description}} - {{/allParams}} - */ - {{/allParams?}} - explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); - {{^bodyParams}} - - /*! \brief Construct a URL without creating a full-fledged job object - * - * This function can be used when a URL for {{camelCaseOperationId}}Job - * is necessary but the job itself isn't. - */ - static QUrl makeRequestUrl(QUrl baseUrl{{#allParams?}}, - {{#allParams}}{{>joinedParamDecl}}{{/allParams}}{{/allParams?}}); - {{/bodyParams}} - {{#responses}}{{#normalResponse?}}{{#allProperties?}} - ~{{camelCaseOperationId}}Job() override; - - // Result properties - {{#allProperties}} - - {{>docCommentShort}} - {{>maybeCrefType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}}; - {{/allProperties}} - -protected: {{#producesNonJson?}} - Status parseReply(QNetworkReply* reply) override; - {{/producesNonJson?}}{{^producesNonJson?}} - Status parseJson(const QJsonDocument& data) override; - {{/producesNonJson?}} - -private: - class Private; - QScopedPointer d; - {{/allProperties?}}{{/normalResponse?}}{{/responses}} -}; - {{/operation}} -{{/operations}} - -} // namespace Quotient -- cgit v1.2.3