aboutsummaryrefslogtreecommitdiff
path: root/gtad
diff options
context:
space:
mode:
Diffstat (limited to 'gtad')
-rw-r--r--gtad/gtad.yaml169
-rw-r--r--gtad/preamble.mustache3
-rw-r--r--gtad/template.cpp.mustache182
-rw-r--r--gtad/template.h.mustache119
4 files changed, 473 insertions, 0 deletions
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: <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:
+ 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: <QtCore/QIODevice>
+ - binary: *ByteStream
+ - +set: { avoidCopy: }
+ +on:
+ - date:
+ type: QDate
+ initializer: QDate::fromString("{{defaultValue}}")
+ imports: <QtCore/QDate>
+ - dateTime:
+ type: QDateTime
+ initializer: QDateTime::fromString("{{defaultValue}}")
+ imports: <QtCore/QDateTime>
+ - //: &QString
+ type: QString
+ initializer: QStringLiteral("{{defaultValue}}")
+ isString:
+ - file: *ByteStream
+ - +set: { avoidCopy: }
+ +on:
+ - object: &QJsonObject { type: QJsonObject, imports: <QtCore/QJsonObject> }
+ - $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<RoomMemberEvent>"
+ imports: '"events/roommemberevent.h"'
+ - /state_event.yaml$/: StateEvents
+ - /room_event.yaml$/: RoomEvents
+ - /event.yaml$/: Events
+ - //: { type: "QVector<{{1}}>", imports: <QtCore/QVector> }
+ - map: # `additionalProperties` in OpenAPI
+ - RoomState:
+ type: "UnorderedMap<QString, {{1}}>"
+ moveOnly:
+ imports: '"util.h"'
+ - /.+/:
+ type: "QHash<QString, {{1}}>"
+ imports: <QtCore/QHash>
+ - //:
+ type: QVariantHash
+ imports: <QtCore/QVariant>
+ - variant: # A sequence `type` (multitype) in OpenAPI
+ - /^string,null|null,string$/: *QString
+ - //: { type: QVariant, imports: <QtCore/QVariant> }
+
+ #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: <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}}{{#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 <QtNetwork/QNetworkReply>
+ {{/producesNonJson?}}
+#include <QtCore/QStringBuilder>
+{{/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<Private> d;
+ {{/allProperties?}}{{/normalResponse?}}{{/responses}}
+};
+ {{/operation}}
+{{/operations}}
+
+} // namespace Quotient