diff options
Diffstat (limited to 'gtad')
-rw-r--r-- | gtad/data.h.mustache | 56 | ||||
-rw-r--r-- | gtad/gtad.yaml | 243 | ||||
-rw-r--r-- | gtad/operation.cpp.mustache | 59 | ||||
-rw-r--r-- | gtad/operation.h.mustache | 128 | ||||
-rw-r--r-- | gtad/preamble.mustache | 3 |
5 files changed, 489 insertions, 0 deletions
diff --git a/gtad/data.h.mustache b/gtad/data.h.mustache new file mode 100644 index 00000000..1b511262 --- /dev/null +++ b/gtad/data.h.mustache @@ -0,0 +1,56 @@ +{{! +SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net> +SPDX-License-Identifier: LGPL-2.1-or-later +}}{{>preamble}} +#pragma once + +#include "converters.h" +{{#imports}} +#include {{_}}{{/imports}} + +namespace Quotient { +{{#models}} + {{#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) + { {{#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, {{name}}& pod) + { {{#parents}} + fillFromJson<{{qualifiedName}}>(jo, pod); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), pod.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, pod.{{nameCamelCase}}); + {{/propertyMap}} + } + {{/out?}} +}; + + {{/model}} +{{/models}} +} // namespace Quotient diff --git a/gtad/gtad.yaml b/gtad/gtad.yaml new file mode 100644 index 00000000..4b05d2d4 --- /dev/null +++ b/gtad/gtad.yaml @@ -0,0 +1,243 @@ +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 + /^/(Location|Protocol|User)$/: 'ThirdParty$1' + # Change some response names + /requestTokenTo.*</data/: response + requestOpenIdToken</data: tokenData + getDevice</data: device + getFilter</data: filter + getProtocols</data: protocols + getOneRoomEvent</data: event + getRoomState</data: events + getPushRule</data: pushRule + # These parameters are deprecated and unused in Quotient; so drop them + login>/user: "" + login>/medium: "" + login>/address: "" + login</home_server: "" + register</home_server: "" + + # Structure inside `types`: + # - swaggerType: <targetTypeSpec> + # OR + # - swaggerType: + # - swaggerFormat: <targetTypeSpec> + # - /swaggerFormatRegEx/: <targetTypeSpec> + # - //: <targetTypeSpec> # default, if the format doesn't mach anything above + # WHERE + # targetTypeSpec = targetType OR + # { type: targetType, imports: <filename OR [ filenames... ]>, <other attributes...> } + # 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: + omittedValue: 'none' # Quotient::none in lib/omittable.h + +on: + - integer: + - int64: qint64 + - int32: qint32 + - //: int + - number: + - float: float + - //: double + - boolean: bool + - string: + - byte: &ByteStream + type: QIODevice* + imports: <QtCore/QIODevice> + - binary: *ByteStream + - +set: { avoidCopy: } + +on: + - date: + type: QDate + initializer: QDate::fromString("{{defaultValue}}") + - dateTime: + type: QDateTime + initializer: QDateTime::fromString("{{defaultValue}}") + - uri: + type: QUrl + initializer: QUrl::fromEncoded("{{defaultValue}}") + - //: &QString + type: QString + initializer: QStringLiteral("{{defaultValue}}") + isString: + - file: *ByteStream + - +set: { avoidCopy: } + +on: + - object: &QJsonObject { type: QJsonObject } + - $ref: + - +set: + moveOnly: + +on: + - /state_event.yaml$/: + type: StateEventPtr + imports: '"events/stateevent.h"' + - /(room|client)_event.yaml$/: + type: RoomEventPtr + imports: '"events/roomevent.h"' + - /event(_without_room_id)?.yaml$/: + type: EventPtr + imports: '"events/event.h"' + - +set: + # This renderer applies to everything actually $ref'ed + # (not substituted) + _importRenderer: '"{{#segments}}{{_}}{{#_join}}/{{/_join}}{{/segments}}.h"' + +on: + - '/^(\./)?definitions/request_email_validation.yaml$/': + title: EmailValidationData + - '/^(\./)?definitions/request_msisdn_validation.yaml$/': + title: MsisdnValidationData + - /_filter.yaml$/: # Event/RoomEventFilters do NOT need Omittable<> + + # Despite being used in two calls, it's more practical to have those + # fields available as getters right from the respective job classes + - /public_rooms_response.yaml$/: { _inline: true } + + # list_public_rooms.yaml (via public_rooms_response.yaml) and + # space_hierarchy.yaml use public_rooms_chunk.yaml as a common base + # structure, adding (space_hiearchy) or overriding + # (public_rooms_response) fields for their purposes. The spec text + # confusingly ends up with having two different structures named + # "PublicRoomsChunk". To make sure the types are distinct in + # libQuotient, this common base is inlined into the actually used + # data structures (that have distinct names) defined + # in space_hierarchy.h and public_rooms_response.h, respectively + - /public_rooms_chunk.yaml$/: { _inline: true } + - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types + - schema: + - getTurnServer<: *QJsonObject # It's used as an opaque JSON object +# - defineFilter>: &Filter # Force folding into a structure +# type: Filter +# imports: '"csapi/definitions/sync_filter.h"' +# - getFilter<: *Filter + - StrippedChildStateEvent: void # only used in an array, see below + - RoomFilter: # A structure inside Filter, same story as with *_filter.yaml + - OneTimeKeys: + type: OneTimeKeys + imports: '"e2ee/e2ee.h"' + - //: *UseOmittable + - array: + - string: QStringList + - +set: { moveOnly: } + +on: + - /^Notification|Result|ChildRoomsChunk$/: "std::vector<{{1}}>" + - /^StrippedChildStateEvent$|state_event.yaml$/: + type: StateEvents + imports: '"events/stateevent.h"' # For StrippedChildStateEvent + - /(room|client)_event.yaml$/: RoomEvents + - /event(_without_room_id)?.yaml$/: Events + - //: "QVector<{{1}}>" + - map: # `additionalProperties` in OpenAPI + - RoomState: + type: "UnorderedMap<QString, {{1}}>" + moveOnly: + - /.+/: "QHash<QString, {{1}}>" + - //: QVariantHash # QJsonObject?.. + - variant: # A sequence `type` or a 'oneOf' group in OpenAPI + - /^string,null|null,string$/: *QString + - //: QVariant + + #operations: + +mustache: +# delimiter: '%| |%' # or something else instead of '{{ }}' + constants: + # Syntax elements used by GTAD +# _quote: '"' # Common quote for left and right +# _leftQuote: '"' +# _rightQuote: '"_ls' + _comment: '//' + copyrightName: Kitsune Ral + copyrightEmail: <kitsune-ral@users.sf.net> + + 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}}" + + maybeCrefJsonObject: + "{{^propertyMap}}const QJsonObject&{{/propertyMap}}\ + {{#propertyMap}}QJsonObject{{/propertyMap}}" + + takeOrValue: + "{{#propertyMap}}take{{/propertyMap}}{{^propertyMap}}value{{/propertyMap}}" + takeOrLoad: "{{#moveOnly}}take{{/moveOnly}}{{^moveOnly}}load{{/moveOnly}}" + + initializeDefaultValue: + "{{#defaultValue}}{{>initializer}}{{/defaultValue}}\ + {{^defaultValue}}{{>omittedValue}}{{/defaultValue}}" + + # No inner indents in folded values! + + joinedParamDecl: >- + {{>maybeCrefType}} {{paramName}} + {{^required?}} = {{>initializeDefaultValue}}{{/required?}}{{>cjoin}} + joinedParamDef: "{{>maybeCrefType}} {{paramName}}{{>cjoin}}" + + passPathAndMaybeQuery: >- + makePath("{{basePathWithoutHost}}"{{#pathParts}}, + {{_}}{{/pathParts}}){{#queryParams?}}, + queryTo{{camelCaseOperationId}}( + {{#queryParams}}{{paramName}}{{>cjoin}}{{/queryParams}}){{/queryParams?}} + + nonInlineResponseSignature: |- + {{>docCommentShort}} + {{>maybeOmittableType}} {{paramName}}(){{^moveOnly}} const{{/moveOnly}} + + # 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...) but we take care of line breaks to maintain + # some sanity even before clang-format + + # This is for structures that don't expect a summary (e.g., JSON schema) + docCommentShort: |- + {{#description}} + /// {{_}}{{/description}} + # For structures with the summary, a common partial for summary is here; + # the main part is different in different places + docCommentSummary: |- + {{#summary}} \brief {{summary}} + *{{/summary}} + + templates: + data: + .h: "{{>data.h.mustache}}" + api: + .h: "{{>operation.h.mustache}}" + .cpp: "{{>operation.cpp.mustache}}" + + #outFilesList: apifiles.txt + diff --git a/gtad/operation.cpp.mustache b/gtad/operation.cpp.mustache new file mode 100644 index 00000000..4b75434c --- /dev/null +++ b/gtad/operation.cpp.mustache @@ -0,0 +1,59 @@ +{{! +SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net> +SPDX-License-Identifier: LGPL-2.1-or-later +}}{{>preamble}} +#include "{{filenameBase}}.h" + +using namespace Quotient; +{{#operations}}{{#operation}} + {{#queryParams?}} + +auto queryTo{{camelCaseOperationId}}( + {{#queryParams}}{{>joinedParamDef}}{{/queryParams}}) +{ + QUrlQuery _q;{{#queryParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_q, + QStringLiteral("{{baseName}}"), {{paramName}});{{/queryParams}} + return _q; +} + {{/queryParams?}} + {{^hasBody?}} + +QUrl {{camelCaseOperationId}}Job::makeRequestUrl(QUrl baseUrl{{#allParams?}}, + {{#allParams}}{{>joinedParamDef}}{{/allParams}}{{/allParams?}}) +{ + return BaseJob::makeRequestUrl(std::move(baseUrl), {{>passPathAndMaybeQuery}}); +} {{/hasBody?}} + +{{camelCaseOperationId}}Job::{{camelCaseOperationId}}Job( + {{#allParams}}{{>joinedParamDef}}{{/allParams}}) + : BaseJob(HttpVerb::{{#_cap}}{{#_tolower}}{{httpMethod}}{{/_tolower}}{{/_cap}}, + {{!object name}}QStringLiteral("{{camelCaseOperationId}}Job"), + {{>passPathAndMaybeQuery}} + {{#skipAuth}}{{#queryParams?}}, {}{{/queryParams?}}, false{{/skipAuth}} ) +{ {{#headerParams}} + setRequestHeader("{{baseName}}", {{paramName}}.toLatin1()); + {{/headerParams}}{{#inlineBody}}{{^propertyMap}}{{^bodyParams?}} + setRequestData({ {{#consumesNonJson?}}{{nameCamelCase}}{{/consumesNonJson? + }}{{^consumesNonJson?}}toJson({{nameCamelCase}}){{/consumesNonJson?}} }); + {{/bodyParams?}}{{/propertyMap}}{{/inlineBody + }}{{^consumesNonJson?}}{{#bodyParams?}} + QJsonObject _dataJson; + {{#propertyMap}} + fillJson(_dataJson, {{nameCamelCase}}); + {{/propertyMap}}{{#inlineBody}} + fillJson<{{>maybeOmittableType}}>(_dataJson, {{paramName}}); + {{/inlineBody}}{{#bodyParams}} + addParam<{{^required?}}IfNotEmpty{{/required?}}>(_dataJson, + QStringLiteral("{{baseName}}"), {{paramName}}); + {{/bodyParams}} + setRequestData({ _dataJson }); + {{/bodyParams?}}{{/consumesNonJson?}}{{#producesNonJson?}} + setExpectedContentTypes({ {{#produces}}"{{_}}"{{>cjoin}}{{/produces}} }); + {{/producesNonJson?}}{{^producesNonJson? + }}{{#responses}}{{#normalResponse?}}{{#properties}}{{#required?}} + addExpectedKey("{{baseName}}"); + {{/required?}}{{/properties}}{{/normalResponse?}}{{/responses + }}{{/producesNonJson?}} +} +{{/operation}}{{/operations}} diff --git a/gtad/operation.h.mustache b/gtad/operation.h.mustache new file mode 100644 index 00000000..063f0bbd --- /dev/null +++ b/gtad/operation.h.mustache @@ -0,0 +1,128 @@ +{{! +SPDX-FileCopyrightText: 2020 Kitsune Ral <Kitsune-Ral@users.sf.net> +SPDX-License-Identifier: LGPL-2.1-or-later +}}{{>preamble}} +#pragma once + +#include "jobs/basejob.h" +{{#imports}} +#include {{_}}{{/imports}} +{{#operations.producesNonJson?}} +#include <QtNetwork/QNetworkReply>{{/operations.producesNonJson?}} + +namespace Quotient { +{{#operations.operation}} + +/*!{{>docCommentSummary}}{{#description}} + * {{_}}{{/description}} + */ +class QUOTIENT_API {{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?}} + /*!{{>docCommentSummary}} + {{#allParams}} + * \param {{nameCamelCase}}{{#description}} + * {{_}}{{/description}}{{#_join}} + * {{/_join}} + {{/allParams}} + */ + {{/allParams?}}{{^allParams?}} + {{#summary}} + /// {{summary}} + {{/summary}} + {{/allParams?}} + explicit {{camelCaseOperationId}}Job({{#allParams}}{{>joinedParamDecl}}{{/allParams}}); + {{^hasBody?}} + + /*! \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?}}); + {{/hasBody?}} + {{#responses}}{{#normalResponse?}}{{#allProperties?}} + + // Result properties + {{#headers}} + + {{>nonInlineResponseSignature}} + { + return reply()->rawHeader("{{baseName}}"); + } + {{/headers}}{{#inlineResponse}} + + {{>docCommentShort}} + {{dataType.name}} {{paramName}}() + {{^moveOnly}}{{^producesNonJson?}} const{{/producesNonJson?}}{{/moveOnly}} + { + return {{#producesNonJson?}}reply(){{/producesNonJson?}} + {{^producesNonJson? + }}fromJson<{{dataType.name}}>(jsonData()){{/producesNonJson? + }}; + } + {{/inlineResponse}}{{#properties}} + + {{!there's nothing in #properties if the response is inline}} + {{>nonInlineResponseSignature}} + { + return {{>takeOrLoad}}FromJson<{{>maybeOmittableType}}>("{{baseName}}"_ls); + } + {{/properties}} + {{/allProperties?}}{{/normalResponse?}}{{/responses}} +}; + {{#models.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<{{name}}{{!of the parent!}}>(jo, result); + {{/parents}}{{#vars}} + fromJson(jo.{{>takeOrValue}}("{{baseName}}"_ls), + result.{{nameCamelCase}}); + {{/vars}}{{#propertyMap}} + fromJson(jo, result.{{nameCamelCase}}); + {{/propertyMap}} + } + {{/out?}} +}; + {{/models.model}} +{{/operations.operation}} + +} // namespace Quotient 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 + */ |