LCOV - code coverage report
Current view: top level - src/lib - shape.cpp (source / functions) Coverage Total Hit
Project: Smithy Qt Lines: 0.0 % 664 0
Version: 0.1.0-pre Functions: 0.0 % 110 0

            Line data    Source code
       1              : // SPDX-FileCopyrightText: 2013-2025 Paul Colby <git@colby.id.au>
       2              : // SPDX-License-Identifier: LGPL-3.0-or-later
       3              : 
       4              : /*!
       5              :  * \file
       6              :  * Defines the Shape and ShapePrivate classes.
       7              :  */
       8              : 
       9              : #include <qtsmithy/shape.h>
      10              : #include "shape_p.h"
      11              : 
      12              : #include <QJsonArray>
      13              : 
      14              : QTSMITHY_BEGIN_NAMESPACE
      15              : 
      16              : /*!
      17              :  * \class Shape
      18              :  *
      19              :  * The Shape class provides a Qt representation of a Smithy semantic shape. That is, essentially a
      20              :  * tagged union or variant class, with some specialised conversions for native Qt types.
      21              :  *
      22              :  * \see https://awslabs.github.io/smithy/2.0/spec/shape.html#semantic-shape
      23              :  */
      24              : 
      25              : /*!
      26              :  * Constructs a new, empty Smithy shape.
      27              :  */
      28            0 : Shape::Shape() : d_ptr(new ShapePrivate(this))
      29            0 : {
      30            0 :     Q_D(Shape);
      31            0 :     d->error = Error::NoError;
      32            0 :     d->type = Type::Undefined;
      33            0 : }
      34              : 
      35            0 : Shape::Shape(const QJsonObject &ast, const ShapeId &id) : d_ptr(new ShapePrivate(this))
      36            0 : {
      37            0 :     Q_D(Shape);
      38            0 :     d->ast = ast;
      39            0 :     d->error = Error::NoError;
      40            0 :     d->id = id;
      41            0 :     d->type = ShapePrivate::getType(ast);
      42            0 :     if (d->type == Type::Undefined) {
      43            0 :         d->error = Error::UndefinedShapeType;
      44            0 :     }
      45              : 
      46              :     // Warn on any unsupported properties.
      47            0 :     const QStringList supportedProperties = ShapePrivate::supportedProperties(d->type);
      48            0 :     const QStringList keys = ast.keys();
      49            0 :     for (const QString &key: keys) {
      50            0 :         if (!supportedProperties.contains(key)) {
      51            0 :             qCWarning(d->lc).noquote() << tr("Ignoring unsupported Shape property: %1").arg(key);
      52            0 :         }
      53            0 :     }
      54              : 
      55              :     // Error on any missing required properties.
      56            0 :     const QStringList requiredProperties = ShapePrivate::requiredProperties(d->type);
      57            0 :     for (const QString &key: requiredProperties) {
      58            0 :         if (!keys.contains(key)) {
      59            0 :             qCritical(d->lc).noquote() << tr("Missing required Shape property: %1").arg(key);
      60            0 :             d->error = Error::MissingRequiredProperty;
      61            0 :         }
      62            0 :     }
      63              : 
      64              :     // Validate all present properties.
      65            0 :     for (auto iter = ast.constBegin(); iter != ast.constEnd(); ++iter) {
      66            0 :         if (!supportedProperties.contains(iter.key())) {
      67            0 :             continue;
      68            0 :         }
      69            0 :         if (!ShapePrivate::validateProperty(iter.key(), iter.value())) {
      70            0 :             if (d->error == Error::NoError) {
      71            0 :                 d->error = Error::InvalidPropertyValue;
      72            0 :             }
      73            0 :         }
      74            0 :     }
      75            0 : }
      76              : 
      77            0 : Shape::Shape(Shape &&other) : d_ptr(new ShapePrivate(this))
      78            0 : {
      79            0 :     Q_D(Shape);
      80            0 :     d->ast = std::move(other.d_ptr->ast);
      81            0 :     d->error = std::move(other.d_ptr->error);
      82            0 :     d->id = std::move(other.d_ptr->id);
      83            0 :     d->type = std::move(other.d_ptr->type);
      84            0 : }
      85              : 
      86            0 : Shape::Shape(const Shape &other) : d_ptr(new ShapePrivate(this))
      87            0 : {
      88            0 :     Q_D(Shape);
      89            0 :     d->ast = other.d_ptr->ast;
      90            0 :     d->error = other.d_ptr->error;
      91            0 :     d->id = other.d_ptr->id;
      92            0 :     d->type = other.d_ptr->type;
      93            0 : }
      94              : 
      95            0 : Shape& Shape::operator=(const Shape &shape)
      96            0 : {
      97            0 :     Q_D(Shape);
      98            0 :     d->ast = shape.d_ptr->ast;
      99            0 :     d->error = shape.d_ptr->error;
     100            0 :     d->id = shape.d_ptr->id;
     101            0 :     d->type = shape.d_ptr->type;
     102            0 :     return *this;
     103            0 : }
     104              : 
     105            0 : Shape& Shape::operator=(const Shape &&shape)
     106            0 : {
     107            0 :     Q_D(Shape);
     108            0 :     d->ast = std::move(shape.d_ptr->ast);
     109            0 :     d->error = std::move(shape.d_ptr->error);
     110            0 :     d->id = std::move(shape.d_ptr->id);
     111            0 :     d->type = std::move(shape.d_ptr->type);
     112            0 :     return *this;
     113            0 : }
     114              : 
     115              : /*!
     116              :  * Destroys this Shape object.
     117              :  */
     118            0 : Shape::~Shape()
     119            0 : {
     120            0 :     delete d_ptr;
     121            0 : }
     122              : 
     123            0 : Shape::Error Shape::error() const
     124            0 : {
     125            0 :     Q_D(const Shape);
     126            0 :     return d->error;
     127            0 : }
     128              : 
     129            0 : bool Shape::isValid() const
     130            0 : {
     131            0 :     Q_D(const Shape);
     132            0 :     return ((d->error == Error::NoError) && (d->type != Type::Undefined));
     133            0 : }
     134              : 
     135              : 
     136            0 : ShapeId Shape::id() const
     137            0 : {
     138            0 :     Q_D(const Shape);
     139            0 :     return d->id;
     140            0 : }
     141              : 
     142            0 : Shape::Type Shape::type() const
     143            0 : {
     144            0 :     Q_D(const Shape);
     145            0 :     return d->type;
     146            0 : }
     147              : 
     148            0 : Shape::TraitsMap Shape::traits() const
     149            0 : {
     150            0 :     Q_D(const Shape);
     151            0 :     return ShapePrivate::getTraitsMap(d->ast, QStringLiteral("traits"));
     152            0 : }
     153              : 
     154            0 : Shape::Member Shape::member() const
     155            0 : {
     156            0 :     Q_D(const Shape);
     157            0 :     return ShapePrivate::getMember(d->ast, QStringLiteral("member"));
     158            0 : }
     159              : 
     160            0 : Shape::Member Shape::key() const
     161            0 : {
     162            0 :     Q_D(const Shape);
     163            0 :     return ShapePrivate::getMember(d->ast, QStringLiteral("key"));
     164            0 : }
     165              : 
     166            0 : Shape::Member Shape::value() const
     167            0 : {
     168            0 :     Q_D(const Shape);
     169            0 :     return ShapePrivate::getMember(d->ast, QStringLiteral("value"));
     170            0 : }
     171              : 
     172            0 : Shape::StringMemberMap Shape::members() const
     173            0 : {
     174            0 :     Q_D(const Shape);
     175            0 :     return ShapePrivate::getStrMemberMap(d->ast, QStringLiteral("members"));
     176            0 : }
     177              : 
     178            0 : QString Shape::version() const
     179            0 : {
     180            0 :     Q_D(const Shape);
     181            0 :     return ShapePrivate::getString(d->ast, QStringLiteral("version"));
     182            0 : }
     183              : 
     184            0 : Shape::ShapeReferences Shape::operations() const
     185            0 : {
     186            0 :     Q_D(const Shape);
     187            0 :     return ShapePrivate::getShapeRefs(d->ast, QStringLiteral("operations"));
     188            0 : }
     189              : 
     190            0 : Shape::ShapeReferences Shape::resources() const {
     191            0 :     Q_D(const Shape);
     192            0 :     return ShapePrivate::getShapeRefs(d->ast, QStringLiteral("resources"));
     193            0 : }
     194              : 
     195            0 : Shape::ShapeReferences Shape::errors() const {
     196            0 :     Q_D(const Shape);
     197            0 :     return ShapePrivate::getShapeRefs(d->ast, QStringLiteral("errors"));
     198            0 : }
     199              : 
     200            0 : ShapeIdStringMap Shape::rename() const
     201            0 : {
     202            0 :     Q_D(const Shape);
     203            0 :     return ShapePrivate::getShapeIdStrMap(d->ast, QStringLiteral("rename"));
     204            0 : }
     205              : 
     206            0 : Shape::StringShapeRefMap Shape::identifiers() const
     207            0 : {
     208            0 :     Q_D(const Shape);
     209            0 :     return ShapePrivate::getStrShapeRefMap(d->ast, QStringLiteral("identifiers"));
     210            0 : }
     211              : 
     212            0 : Shape::StringShapeRefMap Shape::properties() const
     213            0 : {
     214            0 :     Q_D(const Shape);
     215            0 :     return ShapePrivate::getStrShapeRefMap(d->ast, QStringLiteral("properties"));
     216            0 : }
     217              : 
     218            0 : Shape::ShapeReference Shape::create() const
     219            0 : {
     220            0 :     Q_D(const Shape);
     221            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("create"));
     222            0 : }
     223              : 
     224            0 : Shape::ShapeReference Shape::put() const
     225            0 : {
     226            0 :     Q_D(const Shape);
     227            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("put"));
     228            0 : }
     229              : 
     230            0 : Shape::ShapeReference Shape::read() const
     231            0 : {
     232            0 :     Q_D(const Shape);
     233            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("read"));
     234            0 : }
     235              : 
     236            0 : Shape::ShapeReference Shape::update() const
     237            0 : {
     238            0 :     Q_D(const Shape);
     239            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("update"));
     240            0 : }
     241              : 
     242            0 : Shape::ShapeReference Shape::Delete() const
     243            0 : {
     244            0 :     Q_D(const Shape);
     245            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("delete"));
     246            0 : }
     247              : 
     248            0 : Shape::ShapeReference Shape::list() const
     249            0 : {
     250            0 :     Q_D(const Shape);
     251            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("list"));
     252            0 : }
     253              : 
     254            0 : Shape::ShapeReferences Shape::collectionOperations() const
     255            0 : {
     256            0 :     Q_D(const Shape);
     257            0 :     return ShapePrivate::getShapeRefs(d->ast, QStringLiteral("collectionOperations"));
     258            0 : }
     259              : 
     260            0 : Shape::ShapeReference Shape::input() const
     261            0 : {
     262            0 :     Q_D(const Shape);
     263            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("input"));
     264            0 : }
     265              : 
     266            0 : Shape::ShapeReference Shape::output() const
     267            0 : {
     268            0 :     Q_D(const Shape);
     269            0 :     return ShapePrivate::getShapeRef(d->ast, QStringLiteral("output"));
     270            0 : }
     271              : 
     272            0 : Shape::ShapeReferences Shape::mixins() const
     273            0 : {
     274            0 :     Q_D(const Shape);
     275            0 :     return ShapePrivate::getShapeRefs(d->ast, QStringLiteral("mixins"));
     276            0 : }
     277              : 
     278            0 : QJsonObject Shape::rawAst() const
     279            0 : {
     280            0 :     Q_D(const Shape);
     281            0 :     return d->ast;
     282            0 : }
     283              : 
     284              : #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
     285            0 : uint qHash(const Shape::ShapeReference &key, uint seed)
     286              : #else
     287            0 : size_t qHash(const Shape::ShapeReference &key, size_t seed)
     288              : #endif
     289            0 : {
     290            0 :     return ::qHash(key.target, seed);
     291            0 : }
     292              : 
     293            0 : bool operator==(const Shape::ShapeReference &lhs, const Shape::ShapeReference &rhs)
     294            0 : {
     295            0 :     return lhs.target == rhs.target;
     296            0 : }
     297              : 
     298              : /*!
     299              :  * \cond internal
     300              :  * \class ShapePrivate
     301              :  *
     302              :  * The ShapePrivate class provides private implementation for Shape.
     303              :  */
     304              : 
     305              : /*!
     306              :  * \internal
     307              :  * Constructs a new ShapePrivate object with public implementation \a q.
     308              :  */
     309            0 : ShapePrivate::ShapePrivate(Shape * const q) : q_ptr(q)
     310            0 : {
     311              : 
     312            0 : }
     313              : 
     314            0 : Shape::Type ShapePrivate::getType(const QJsonObject &ast)
     315            0 : {
     316            0 :     const QJsonValue value = ast.value(QLatin1String("type"));
     317            0 :     if (value.isUndefined()) {
     318            0 :         qCCritical(lc).noquote() << tr("Shape has no type property");
     319            0 :         return Shape::Type::Undefined;
     320            0 :     }
     321            0 :     if (value.type() != QJsonValue::String) {
     322            0 :         qCCritical(lc).noquote() << tr("Shape type property is not a JSON string:") << value;
     323            0 :         return Shape::Type::Undefined;
     324            0 :     }
     325            0 :     return getType(value.toString());
     326            0 : }
     327              : 
     328            0 : Shape::Type ShapePrivate::getType(const QString &type)
     329            0 : {
     330              :     // Simple Types
     331            0 :     if (type == QLatin1String("blob"))       return Shape::Type::Blob;
     332            0 :     if (type == QLatin1String("boolean"))    return Shape::Type::Boolean;
     333            0 :     if (type == QLatin1String("string"))     return Shape::Type::String;
     334            0 :     if (type == QLatin1String("enum"))       return Shape::Type::Enum;
     335            0 :     if (type == QLatin1String("byte"))       return Shape::Type::Byte;
     336            0 :     if (type == QLatin1String("short"))      return Shape::Type::Short;
     337            0 :     if (type == QLatin1String("integer"))    return Shape::Type::Integer;
     338            0 :     if (type == QLatin1String("intEnum"))    return Shape::Type::IntEnum;
     339            0 :     if (type == QLatin1String("long"))       return Shape::Type::Long;
     340            0 :     if (type == QLatin1String("float"))      return Shape::Type::Float;
     341            0 :     if (type == QLatin1String("double"))     return Shape::Type::Double;
     342            0 :     if (type == QLatin1String("bigInteger")) return Shape::Type::BigInteger;
     343            0 :     if (type == QLatin1String("bigDecimal")) return Shape::Type::BigDecimal;
     344            0 :     if (type == QLatin1String("timestamp"))  return Shape::Type::Timestamp;
     345            0 :     if (type == QLatin1String("document"))   return Shape::Type::Document;
     346              : 
     347              :     // Aggregate Types
     348            0 :     if (type == QLatin1String("list"))       return Shape::Type::List;
     349            0 :     if (type == QLatin1String("set"))        return Shape::Type::Set;
     350            0 :     if (type == QLatin1String("map"))        return Shape::Type::Map;
     351            0 :     if (type == QLatin1String("structure"))  return Shape::Type::Structure;
     352            0 :     if (type == QLatin1String("union"))      return Shape::Type::Union;
     353              : 
     354              : 
     355              :     // Service Types
     356            0 :     if (type == QLatin1String("service"))    return Shape::Type::Service;
     357            0 :     if (type == QLatin1String("operation"))  return Shape::Type::Operation;
     358            0 :     if (type == QLatin1String("resource"))   return Shape::Type::Resource;
     359              : 
     360              :     // Special Types
     361            0 :     if (type == QLatin1String("apply"))      return Shape::Type::Apply;
     362              : 
     363            0 :     qCWarning(lc).noquote() << tr("Unknown shape type: %1").arg(type);
     364            0 :     return Shape::Type::Undefined;
     365            0 : }
     366              : 
     367            0 : Shape::Member ShapePrivate::getMember(const QJsonObject &ast, const QString &name)
     368            0 : {
     369            0 :     Shape::Member member;
     370            0 :     Q_UNUSED(ast);
     371            0 :     Q_UNUSED(name);
     372            0 :     Q_UNIMPLEMENTED();
     373            0 :     return member;
     374            0 : }
     375              : 
     376            0 : ShapeIdStringMap ShapePrivate::getShapeIdStrMap(const QJsonObject &ast, const QString &name)
     377            0 : {
     378            0 :     ShapeIdStringMap shapeIdMap;
     379            0 :     Q_UNUSED(ast);
     380            0 :     Q_UNUSED(name);
     381            0 :     Q_UNIMPLEMENTED();
     382            0 :     return shapeIdMap;
     383            0 : }
     384              : 
     385            0 : Shape::ShapeReference ShapePrivate::getShapeRef(const QJsonObject &ast, const QString &name)
     386            0 : {
     387            0 :     const auto iter = ast.constFind(name);
     388            0 :     if (iter == ast.constEnd()) {
     389            0 :         return Shape::ShapeReference{};
     390            0 :     }
     391            0 :     const QJsonObject shapeRefObj = iter->toObject();
     392            0 :     return Shape::ShapeReference{ shapeRefObj.value(QLatin1String("target")).toString() };
     393            0 : }
     394              : 
     395            0 : Shape::ShapeReferences ShapePrivate::getShapeRefs(const QJsonObject &ast, const QString &name)
     396            0 : {
     397            0 :     const auto iter = ast.constFind(name);
     398            0 :     if (iter == ast.constEnd()) {
     399            0 :         return Shape::ShapeReferences{};
     400            0 :     }
     401            0 :     const QJsonArray shapeRefsArray = iter->toArray();
     402            0 :     Shape::ShapeReferences shapeRefs;
     403            0 :     for (const QJsonValue &shapeRef: shapeRefsArray) {
     404            0 :         shapeRefs.insert(Shape::ShapeReference{
     405            0 :             shapeRef.toObject().value(QLatin1String("target")).toString()
     406            0 :         });
     407            0 :     }
     408            0 :     return shapeRefs;
     409            0 : }
     410              : 
     411            0 : Shape::StringMemberMap ShapePrivate::getStrMemberMap(const QJsonObject &ast, const QString &name)
     412            0 : {
     413            0 :     Shape::StringMemberMap memberMap;
     414            0 :     Q_UNUSED(ast);
     415            0 :     Q_UNUSED(name);
     416            0 :     Q_UNIMPLEMENTED();
     417            0 :     return memberMap;
     418            0 : }
     419              : 
     420            0 : Shape::StringShapeRefMap ShapePrivate::getStrShapeRefMap(const QJsonObject &ast, const QString &name)
     421            0 : {
     422            0 :     Shape::StringShapeRefMap shapeRefMap;
     423            0 :     Q_UNUSED(ast);
     424            0 :     Q_UNUSED(name);
     425            0 :     Q_UNIMPLEMENTED();
     426            0 :     return shapeRefMap;
     427            0 : }
     428              : 
     429            0 : Shape::TraitsMap ShapePrivate::getTraitsMap(const QJsonObject &ast, const QString &name)
     430            0 : {
     431            0 :     Shape::TraitsMap traitsMap;
     432            0 :     const QJsonObject traits = ast.value(name).toObject();
     433            0 :     for (auto iter = traits.constBegin(); iter != traits.constEnd(); ++iter) {
     434            0 :         traitsMap.insert(iter.key(), iter.value());
     435            0 :     }
     436            0 :     return traitsMap;
     437            0 : }
     438              : 
     439            0 : QString ShapePrivate::getString(const QJsonObject &ast, const QString &name)
     440            0 : {
     441            0 :     const auto iter = ast.constFind(name);
     442            0 :     return (iter == ast.constEnd()) ? QString() : iter->toString();
     443            0 : }
     444              : 
     445            0 : QStringList ShapePrivate::supportedProperties(const Shape::Type &type)
     446            0 : {
     447            0 :     switch (type) {
     448            0 :     case Shape::Type::Undefined:
     449            0 :         return QStringList{};
     450              : 
     451              :     // Simple Types:
     452            0 :     case Shape::Type::Blob:
     453            0 :     case Shape::Type::Boolean:
     454            0 :     case Shape::Type::String:
     455              :   //case Shape::Type::Enum: <- Same as strucutre and union below.
     456            0 :     case Shape::Type::Byte:
     457            0 :     case Shape::Type::Short:
     458            0 :     case Shape::Type::Integer:
     459              :   //case Shape::Type::IntEnum: <- Same as strucutre and union below.
     460            0 :     case Shape::Type::Long:
     461            0 :     case Shape::Type::Float:
     462            0 :     case Shape::Type::Double:
     463            0 :     case Shape::Type::BigInteger:
     464            0 :     case Shape::Type::BigDecimal:
     465            0 :     case Shape::Type::Timestamp:
     466            0 :     case Shape::Type::Document:
     467            0 :         return QStringList{
     468            0 :             QLatin1String("type"),
     469            0 :             QLatin1String("traits"),
     470            0 :             QLatin1String("mixins"),
     471            0 :         };
     472            0 :         break;
     473              : 
     474              :     // Aggregate Types
     475            0 :     case Shape::Type::List:
     476              :   //case Shape::Type::Set:  <- Set is synonym for List.
     477            0 :         return QStringList{
     478            0 :             QLatin1String("type"),
     479            0 :             QLatin1String("traits"),
     480            0 :             QLatin1String("member"),
     481            0 :             QLatin1String("mixins"),
     482            0 :         };
     483            0 :         break;
     484            0 :     case Shape::Type::Map:
     485            0 :         return QStringList{
     486            0 :             QLatin1String("type"),
     487            0 :             QLatin1String("traits"),
     488            0 :             QLatin1String("key"),
     489            0 :             QLatin1String("value"),
     490            0 :             QLatin1String("mixins"),
     491            0 :         };
     492            0 :         break;
     493            0 :     case Shape::Type::Structure:
     494            0 :     case Shape::Type::Union:
     495            0 :     case Shape::Type::Enum:
     496            0 :     case Shape::Type::IntEnum:
     497            0 :         return QStringList{
     498            0 :             QLatin1String("type"),
     499            0 :             QLatin1String("traits"),
     500            0 :             QLatin1String("members"),
     501            0 :             QLatin1String("mixins"),
     502            0 :         };
     503              : 
     504              :     // Service Types
     505            0 :     case Shape::Type::Service:
     506            0 :         return QStringList{
     507            0 :             QLatin1String("type"),
     508            0 :             QLatin1String("traits"),
     509            0 :             QLatin1String("version"),
     510            0 :             QLatin1String("operations"),
     511            0 :             QLatin1String("resources"),
     512            0 :             QLatin1String("errors"),
     513            0 :             QLatin1String("rename"),
     514            0 :             QLatin1String("mixins"),
     515            0 :         };
     516            0 :     case Shape::Type::Operation:
     517            0 :         return QStringList{
     518            0 :             QLatin1String("type"),
     519            0 :             QLatin1String("traits"),
     520            0 :             QLatin1String("input"),
     521            0 :             QLatin1String("output"),
     522            0 :             QLatin1String("errors"),
     523            0 :             QLatin1String("mixins"),
     524            0 :         };
     525            0 :     case Shape::Type::Resource:
     526            0 :         return QStringList{
     527            0 :             QLatin1String("type"),
     528            0 :             QLatin1String("traits"),
     529            0 :             QLatin1String("identifiers"),
     530            0 :             QLatin1String("properties"),
     531            0 :             QLatin1String("create"),
     532            0 :             QLatin1String("put"),
     533            0 :             QLatin1String("read"),
     534            0 :             QLatin1String("update"),
     535            0 :             QLatin1String("delete"),
     536            0 :             QLatin1String("list"),
     537            0 :             QLatin1String("operations"),
     538            0 :             QLatin1String("collectionOperations"),
     539            0 :             QLatin1String("resources"),
     540            0 :             QLatin1String("mixins"),
     541            0 :         };
     542              : 
     543              :     // Special Types
     544            0 :     case Shape::Type::Apply:
     545            0 :         return QStringList{
     546            0 :             QLatin1String("type"),
     547            0 :             QLatin1String("traits"),
     548            0 :         };
     549            0 :         break;
     550            0 :     }
     551            0 :     qCWarning(lc).noquote() << tr("Unknown shape type: 0x%1").arg((int)type, 0, 16);
     552            0 :     return QStringList{};
     553            0 : }
     554              : 
     555            0 : QStringList ShapePrivate::requiredProperties(const Shape::Type &type)
     556            0 : {
     557            0 :     switch (type) {
     558            0 :     case Shape::Type::List:
     559              :   //case Shape::Type::Set: <- Set is synonym for List.
     560            0 :         return QStringList{
     561            0 :             QLatin1String("type"),
     562            0 :             QLatin1String("member"),
     563            0 :         };
     564            0 :         break;
     565              : 
     566            0 :     case Shape::Type::Map:
     567            0 :         return QStringList{
     568            0 :             QLatin1String("type"),
     569            0 :             QLatin1String("key"),
     570            0 :             QLatin1String("value"),
     571            0 :         };
     572            0 :         break;
     573              : 
     574            0 :     case Shape::Type::Union:
     575            0 :     case Shape::Type::Enum:
     576            0 :     case Shape::Type::IntEnum:
     577            0 :         return QStringList{
     578            0 :             QLatin1String("type"),
     579            0 :             QLatin1String("members"),
     580            0 :         };
     581              : 
     582            0 :     case Shape::Type::Service:
     583            0 :         return QStringList{
     584            0 :             QLatin1String("type"),
     585            0 :             QLatin1String("version"),
     586            0 :         };
     587              : 
     588            0 :     case Shape::Type::Apply:
     589            0 :         return QStringList{
     590            0 :             QLatin1String("type"),
     591            0 :             QLatin1String("traits"),
     592            0 :         };
     593            0 :         break;
     594              : 
     595            0 :     default:
     596            0 :         return QStringList{
     597            0 :             QLatin1String("type"),
     598            0 :         };
     599            0 :     }
     600            0 : }
     601              : 
     602            0 : bool ShapePrivate::validateIdentifier(const QString &id)
     603            0 : {
     604              :     // Lazily relying on the fact that ShapeId will validate the idenitifer regex pattern for now.
     605            0 :     const ShapeId shapeId(id);
     606            0 :     return ((shapeId.isValid()) && (!shapeId.hasNameSpace()) && (!shapeId.hasMemberName()));
     607            0 : }
     608              : 
     609            0 : bool ShapePrivate::validateProperty(const QString &name, const QJsonValue &value)
     610            0 : {
     611              :     // Type string
     612            0 :     if (name == QLatin1String("type")) {
     613            0 :         if (!value.isString()) {
     614            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON string").arg(name);
     615            0 :             return false;
     616            0 :         }
     617            0 :         return (getType(value.toString()) != Shape::Type::Undefined);
     618            0 :     }
     619              : 
     620              :     // TraitsMap
     621            0 :     else if (name == QLatin1String("traits")) {
     622            0 :         if (!value.isObject()) {
     623            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON object").arg(name);
     624            0 :             return false;
     625            0 :         }
     626            0 :         const QJsonObject traits = value.toObject();
     627            0 :         for (auto iter = traits.constBegin(); iter != traits.constEnd(); ++iter) {
     628            0 :             const ShapeId id(iter.key());
     629            0 :             if (!id.isValid()) {
     630            0 :                 qCCritical(lc).noquote() << tr("%1 property has trait with invalid shape ID %2")
     631            0 :                                             .arg(name, iter.key());
     632            0 :                 return false;
     633            0 :             }
     634            0 :             if (id.isRelativeShapeId()) {
     635            0 :                 qCCritical(lc).noquote() << tr("%1 property has trait with relative shape ID %2")
     636            0 :                                             .arg(name, iter.key());
     637            0 :                 return false;
     638            0 :             }
     639              :             // It doesn't matter (at this point) what type iter->value() is.
     640            0 :         }
     641            0 :     }
     642              : 
     643              :     // Member (aka ShapeReference plus optional TraitsMap)
     644            0 :     else if ((name == QLatin1String("member")) ||
     645            0 :              (name == QLatin1String("key")) ||
     646            0 :              (name == QLatin1String("value")))
     647            0 :     {
     648            0 :         if (!value.isObject()) {
     649            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON object").arg(name);
     650            0 :             return false;
     651            0 :         }
     652            0 :         if (!validateProperty(QStringLiteral("ShapeReference"), value)) {
     653            0 :             qCCritical(lc).noquote() << tr("%1 property is not valid ShapeReference").arg(name);
     654            0 :             return false;
     655            0 :         }
     656            0 :         const QJsonObject member = value.toObject();
     657            0 :         const auto traits = member.constFind(QLatin1String("traits")); // Optional.
     658            0 :         if ((traits != member.constEnd()) && (!validateProperty(traits.key(), traits.value()))) {
     659            0 :             qCCritical(lc).noquote() << tr("%1 property has invalid traits property").arg(name);
     660            0 :             return false;
     661            0 :         }
     662            0 :     }
     663              : 
     664              :     // Members (ie Set of Member instances)
     665            0 :     else if (name == QLatin1String("members")) {
     666            0 :         if (!value.isObject()) {
     667            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON object").arg(name);
     668            0 :             return false;
     669            0 :         }
     670            0 :         QStringList names;
     671            0 :         const QJsonObject members = value.toObject();
     672            0 :         for (auto iter = members.constBegin(); iter != members.constEnd(); ++iter) {
     673            0 :             if (!validateIdentifier(iter.key())) {
     674            0 :                 qCCritical(lc).noquote() << tr("%1 property has invalid member name %2")
     675            0 :                                             .arg(name, iter.key());
     676            0 :                 return false;
     677            0 :             }
     678            0 :             if (names.contains(iter.key().toLower())) {
     679            0 :                 qCCritical(lc).noquote() << tr("%1 property has non-unique member name %2")
     680            0 :                                             .arg(name, iter.key().toLower());
     681            0 :                 return false;
     682            0 :             }
     683            0 :             names.append(iter.key().toLower());
     684            0 :             if (!validateProperty(QLatin1String("member"), iter.value())) {
     685            0 :                 qCCritical(lc).noquote() << tr("%1 property has invalid value for %2 property")
     686            0 :                                             .arg(name, iter.key());
     687            0 :                 return false;
     688            0 :             }
     689            0 :         }
     690            0 :     }
     691              : 
     692              :     // Plain string
     693            0 :     else if (name == QLatin1String("version")) {
     694            0 :         if (!value.isString()) {
     695            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON string").arg(name);
     696            0 :             return false;
     697            0 :         }
     698              :         // No further validation required; the string is free-form.
     699            0 :     }
     700              : 
     701              :     // ShapeReferences
     702            0 :     else if ((name == QLatin1String("operations")) ||
     703            0 :              (name == QLatin1String("collectionOperations")) ||
     704            0 :              (name == QLatin1String("resources")) ||
     705            0 :              (name == QLatin1String("errors")) ||
     706            0 :              (name == QLatin1String("mixins"))) {
     707            0 :         if (!value.isArray()) {
     708            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON array").arg(name);
     709            0 :             return false;
     710            0 :         }
     711            0 :         const QJsonArray shapeRefs = value.toArray();
     712            0 :         for (const QJsonValue &shapeRef: shapeRefs) {
     713            0 :             if (!validateProperty(QLatin1String("ShapeReference"), shapeRef)) {
     714            0 :                 qCCritical(lc).noquote() << tr("%1 property has invalid entry").arg(name);
     715            0 :             }
     716            0 :         }
     717            0 :     }
     718              : 
     719              :     // ShapeIdStringMap
     720            0 :     else if (name == QLatin1String("rename")) {
     721            0 :         if (!value.isObject()) {
     722            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON object").arg(name);
     723            0 :             return false;
     724            0 :         }
     725            0 :         const QJsonObject rename = value.toObject();
     726            0 :         for (auto iter = rename.constBegin(); iter != rename.constEnd(); ++iter) {
     727            0 :             const ShapeId shapeId(iter.key());
     728            0 :             if (!shapeId.isValid()) {
     729            0 :                 qCCritical(lc).noquote() << tr("%1 property has invalid shape ID %2")
     730            0 :                                             .arg(name, iter.key());
     731            0 :                 return false;
     732            0 :             }
     733            0 :             if (!iter.value().isString()) {
     734            0 :                 qCCritical(lc).noquote() << tr("%1 property has shape ID %2 with invalid value")
     735            0 :                                             .arg(name, iter.key());
     736            0 :                 return false;
     737            0 :             }
     738            0 :             const QString indentifier = iter.value().toString();
     739            0 :             if (validateIdentifier(indentifier)) {
     740            0 :                 qCCritical(lc).noquote() << tr("%1 property has shape ID %2 with invalid identifier %3")
     741            0 :                                             .arg(name, iter.key(), indentifier);
     742            0 :                 return false;
     743            0 :             }
     744            0 :         }
     745            0 :     }
     746              : 
     747              :     // ShapeReference
     748            0 :     else if ((name == QLatin1String("ShapeReference")) ||
     749            0 :              (name == QLatin1String("create")) ||
     750            0 :              (name == QLatin1String("put")) ||
     751            0 :              (name == QLatin1String("read")) ||
     752            0 :              (name == QLatin1String("update")) ||
     753            0 :              (name == QLatin1String("delete")) ||
     754            0 :              (name == QLatin1String("list")) ||
     755            0 :              (name == QLatin1String("input")) ||
     756            0 :              (name == QLatin1String("output"))) {
     757            0 :         if (!value.isObject()) {
     758            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON object").arg(name);
     759            0 :             return false;
     760            0 :         }
     761            0 :         const QJsonObject shapeRef = value.toObject();
     762            0 :         const auto target = shapeRef.constFind(QLatin1String("target")); // Required.
     763            0 :         if (target == shapeRef.constEnd()) {
     764            0 :             qCCritical(lc).noquote() << tr("%1 property has no target property").arg(name);
     765            0 :             return false;
     766            0 :         }
     767            0 :         if (!target.value().isString()) {
     768            0 :             qCCritical(lc).noquote() << tr("%1 property has target value that not a JSON string")
     769            0 :                                         .arg(name);
     770            0 :             return false;
     771            0 :         }
     772            0 :         const QString shapeIdString = target.value().toString();
     773            0 :         const ShapeId shapeId(shapeIdString);
     774            0 :         if (!shapeId.isValid()) {
     775            0 :             qCCritical(lc).noquote() << tr("%1 property has target with invalid shape ID %2")
     776            0 :                                         .arg(name, shapeIdString);
     777            0 :             return false;
     778            0 :         }
     779            0 :         if (shapeId.isRelativeShapeId()) {
     780            0 :             qCCritical(lc).noquote() << tr("%1 property has target with relative shape ID %2")
     781            0 :                                         .arg(name, shapeIdString);
     782            0 :             return false;
     783            0 :         }
     784            0 :     }
     785              : 
     786              :     // StringShapeRefMap
     787            0 :     else if ((name == QLatin1String("identifiers")) ||
     788            0 :              (name == QLatin1String("properties"))) {
     789            0 :         if (!value.isObject()) {
     790            0 :             qCCritical(lc).noquote() << tr("%1 property is not a JSON object").arg(name);
     791            0 :             return false;
     792            0 :         }
     793            0 :         const QJsonObject identifiers = value.toObject();
     794            0 :         for (auto iter = identifiers.constBegin(); iter != identifiers.constEnd(); ++iter) {
     795            0 :             if (!validateIdentifier(iter.key())) {
     796            0 :                 qCCritical(lc).noquote() << tr("%1 property has invalid member name %2")
     797            0 :                                             .arg(name, iter.key());
     798            0 :                 return false;
     799            0 :             }
     800            0 :             if (!validateProperty(QLatin1String("ShapeReference"), iter.value())) {
     801            0 :                 qCCritical(lc).noquote() << tr("%1 property has invalid value for %2 property")
     802            0 :                                             .arg(name, iter.key());
     803            0 :                 return false;
     804            0 :             }
     805            0 :         }
     806            0 :     }
     807              : 
     808            0 :     else {
     809            0 :         qCWarning(lc).noquote() << tr("Validation of %1 property not yet implemented").arg(name);
     810            0 :         return false;
     811            0 :     }
     812            0 :     return true;
     813            0 : }
     814              : 
     815              : /// \endcond
     816              : 
     817              : QTSMITHY_END_NAMESPACE
        

Generated by: LCOV version 2.2-1