Skip to content
Snippets Groups Projects
DapRpcMessage.cpp 12.2 KiB
Newer Older
#include "DapRpcMessage.h"

class DapRpcMessagePrivate : public QSharedData
{
public:
    DapRpcMessagePrivate();
    ~DapRpcMessagePrivate();
    DapRpcMessagePrivate(const DapRpcMessagePrivate &aDapRpcMessagePrivate);

    void initializeWithObject(const QJsonObject &aMessage);
    static DapRpcMessage createBasicRequest(const QString &asMethod, const QJsonArray &aParams);
    static DapRpcMessage createBasicRequest(const QString &asMethod,
                                              const QJsonObject &aNamedParameters);

    DapRpcMessage::Type m_type;
    QScopedPointer<QJsonObject> m_pObject;

    static int uniqueRequestCounter;
};

int DapRpcMessagePrivate::uniqueRequestCounter = 0;

DapRpcMessagePrivate::DapRpcMessagePrivate()
    : m_type(DapRpcMessage::Invalid),
      m_pObject(nullptr)
{
}

DapRpcMessagePrivate::DapRpcMessagePrivate(const DapRpcMessagePrivate &aDapRpcMessagePrivate)
    : QSharedData(aDapRpcMessagePrivate),
      m_type(aDapRpcMessagePrivate.m_type),
      m_pObject(aDapRpcMessagePrivate.m_pObject ? new QJsonObject(*aDapRpcMessagePrivate.m_pObject) : nullptr)
{
}

void DapRpcMessagePrivate::initializeWithObject(const QJsonObject &aMessage)
{
    m_pObject.reset(new QJsonObject(aMessage));
    if (aMessage.contains(QLatin1String("id"))) {
        if (aMessage.contains(QLatin1String("result")) ||
            aMessage.contains(QLatin1String("error"))) {
            if (aMessage.contains(QLatin1String("error")) &&
                !aMessage.value(QLatin1String("error")).isNull())
                m_type = DapRpcMessage::Error;
            else
                m_type = DapRpcMessage::Response;
        } else if (aMessage.contains(QLatin1String("method"))) {
            m_type = DapRpcMessage::Request;
        }
    } else {
        if (aMessage.contains(QLatin1String("method")))
            m_type = DapRpcMessage::Notification;
    }
}

DapRpcMessagePrivate::~DapRpcMessagePrivate()
{
}

DapRpcMessage::DapRpcMessage()
    : d(new DapRpcMessagePrivate)
{
    d->m_pObject.reset(new QJsonObject);
}

DapRpcMessage::DapRpcMessage(const DapRpcMessage &aDapRPCMessage)
    : d(aDapRPCMessage.d)
{
}

DapRpcMessage::~DapRpcMessage()
{
}

DapRpcMessage &DapRpcMessage::operator=(const DapRpcMessage &aDapRPCMessage)
{
    d = aDapRPCMessage.d;
    return *this;
}

bool DapRpcMessage::operator==(const DapRpcMessage &aDapRpcMessage) const
{
    if (aDapRpcMessage.d == d)
        return true;

    if (aDapRpcMessage.type() == type()) {
        if (aDapRpcMessage.type() == DapRpcMessage::Error) {
            return (aDapRpcMessage.errorCode() == errorCode() &&
                    aDapRpcMessage.errorMessage() == errorMessage() &&
                    aDapRpcMessage.errorData() == errorData());
        } else {
            if (aDapRpcMessage.type() == DapRpcMessage::Notification) {
                return (aDapRpcMessage.method() == method() &&
                        aDapRpcMessage.params() == params());
            } else {
                return (aDapRpcMessage.id() == id() &&
                        aDapRpcMessage.method() == method() &&
                        aDapRpcMessage.params() == params());
            }
        }
    }

    return false;
}

DapRpcMessage DapRpcMessage::fromJson(const QByteArray &aData)
{
    DapRpcMessage result;
    QJsonParseError error;
    QJsonDocument document = QJsonDocument::fromJson(aData, &error);
    if (error.error != QJsonParseError::NoError) {
        qJsonRpcDebug() << error.errorString();
        return result;
    }

    if (!document.isObject()) {
        qJsonRpcDebug() << "invalid message: " << aData;
        return result;
    }

    result.d->initializeWithObject(document.object());
    return result;
}

DapRpcMessage DapRpcMessage::fromObject(const QJsonObject &aObject)
{
    DapRpcMessage result;
    result.d->initializeWithObject(aObject);
    return result;
}

QJsonObject DapRpcMessage::toObject() const
{
    if (d->m_pObject)
        return QJsonObject(*d->m_pObject);
    return QJsonObject();
}

QByteArray DapRpcMessage::toJson() const
{
    if (d->m_pObject) {
        QJsonDocument doc(*d->m_pObject);
        return doc.toJson();
    }

    return QByteArray();
}

bool DapRpcMessage::isValid() const
{
    return d->m_type != DapRpcMessage::Invalid;
}

DapRpcMessage::Type DapRpcMessage::type() const
{
    return d->m_type;
}

DapRpcMessage DapRpcMessagePrivate::createBasicRequest(const QString &asMethod, const QJsonArray &aParams)
{
    DapRpcMessage request;
    request.d->m_pObject->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
    request.d->m_pObject->insert(QLatin1String("method"), asMethod);
    if (!aParams.isEmpty())
        request.d->m_pObject->insert(QLatin1String("params"), aParams);
    return request;
}

DapRpcMessage DapRpcMessagePrivate::createBasicRequest(const QString &asMethod,
                                                           const QJsonObject &aNamedParameters)
{
    DapRpcMessage request;
    request.d->m_pObject->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
    request.d->m_pObject->insert(QLatin1String("method"), asMethod);
    if (!aNamedParameters.isEmpty())
        request.d->m_pObject->insert(QLatin1String("params"), aNamedParameters);
    return request;
}

DapRpcMessage DapRpcMessage::createRequest(const QString &asMethod, const QJsonArray &aParams)
{
    DapRpcMessage request = DapRpcMessagePrivate::createBasicRequest(asMethod, aParams);
    request.d->m_type = DapRpcMessage::Request;
    DapRpcMessagePrivate::uniqueRequestCounter++;
    request.d->m_pObject->insert(QLatin1String("id"), DapRpcMessagePrivate::uniqueRequestCounter);
    return request;
}

DapRpcMessage DapRpcMessage::createRequest(const QString &asMethod, const QJsonValue &aParam)
{
    QJsonArray params;
    params.append(aParam);
    return createRequest(asMethod, params);
}

DapRpcMessage DapRpcMessage::createRequest(const QString &asMethod,
                                               const QJsonObject &aNamedParameters)
{
    DapRpcMessage request =
        DapRpcMessagePrivate::createBasicRequest(asMethod, aNamedParameters);
    request.d->m_type = DapRpcMessage::Request;
    DapRpcMessagePrivate::uniqueRequestCounter++;
    request.d->m_pObject->insert(QLatin1String("id"), DapRpcMessagePrivate::uniqueRequestCounter);
    return request;
}

DapRpcMessage DapRpcMessage::createRequest(const QString& asMethod, const QByteArray& aStream)
{
    DapRpcMessage request = createRequest(asMethod, QJsonValue::fromVariant(aStream));
    return request;
}

DapRpcMessage DapRpcMessage::createNotification(const QString &asMethod, const QJsonArray &aParams)
{
    DapRpcMessage notification = DapRpcMessagePrivate::createBasicRequest(asMethod, aParams);
    notification.d->m_type = DapRpcMessage::Notification;
    return notification;
}

DapRpcMessage DapRpcMessage::createNotification(const QString &asMethod, const QJsonValue &aParam)
{
    QJsonArray params;
    params.append(aParam);
    return createNotification(asMethod, params);
}

DapRpcMessage DapRpcMessage::createNotification(const QString &asMethod,
                                                    const QJsonObject &aNamedParameters)
{
    DapRpcMessage notification =
        DapRpcMessagePrivate::createBasicRequest(asMethod, aNamedParameters);
    notification.d->m_type = DapRpcMessage::Notification;
    return notification;
}

DapRpcMessage DapRpcMessage::createNotification(const QString& asMethod, const QByteArray& aStream)
{
    DapRpcMessage notification = createNotification(asMethod, QJsonValue::fromVariant(aStream));
    return notification;
}

DapRpcMessage DapRpcMessage::createResponse(const QJsonValue &aResult) const
{
    DapRpcMessage response;
    if (d->m_pObject->contains(QLatin1String("id"))) {
        QJsonObject *object = response.d->m_pObject.data();
        object->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
        object->insert(QLatin1String("id"), d->m_pObject->value(QLatin1String("id")));
        object->insert(QLatin1String("result"), aResult);
        response.d->m_type = DapRpcMessage::Response;
    }

    return response;
}

DapRpcMessage DapRpcMessage::createErrorResponse(DapErrorCode aCode,
                                                     const QString &asMessage,
                                                     const QJsonValue &aData) const
{
    DapRpcMessage response;
    QJsonObject error;
    error.insert(QLatin1String("code"), aCode);
    if (!asMessage.isEmpty())
        error.insert(QLatin1String("message"), asMessage);
    if (!aData.isUndefined())
        error.insert(QLatin1String("data"), aData);

    response.d->m_type = DapRpcMessage::Error;
    QJsonObject *object = response.d->m_pObject.data();
    object->insert(QLatin1String("jsonrpc"), QLatin1String("2.0"));
    if (d->m_pObject->contains(QLatin1String("id")))
        object->insert(QLatin1String("id"), d->m_pObject->value(QLatin1String("id")));
    else
        object->insert(QLatin1String("id"), 0);
    object->insert(QLatin1String("error"), error);
    return response;
}

int DapRpcMessage::id() const
{
    if (d->m_type == DapRpcMessage::Notification || !d->m_pObject)
        return -1;

    const QJsonValue &value = d->m_pObject->value(QLatin1String("id"));
    if (value.isString())
        return value.toString().toInt();
    return value.toInt();
}

QString DapRpcMessage::method() const
{
    if (d->m_type == DapRpcMessage::Response || !d->m_pObject)
        return QString();

    return d->m_pObject->value(QLatin1String("method")).toString();
}

QJsonValue DapRpcMessage::params() const
{
    if (d->m_type == DapRpcMessage::Response || d->m_type == DapRpcMessage::Error)
        return QJsonValue(QJsonValue::Undefined);
    if (!d->m_pObject)
        return QJsonValue(QJsonValue::Undefined);

    return d->m_pObject->value(QLatin1String("params"));
}

QJsonValue DapRpcMessage::toJsonValue() const
{
    if (d->m_type != DapRpcMessage::Response || !d->m_pObject)
        return QJsonValue(QJsonValue::Undefined);

    return d->m_pObject->value(QLatin1String("result"));
}

QByteArray DapRpcMessage::toByteArray() const
{
    QJsonValue value = toJsonValue();
    return QByteArray::fromHex(value.toVariant().toByteArray());
}

int DapRpcMessage::errorCode() const
{
    if (d->m_type != DapRpcMessage::Error || !d->m_pObject)
        return 0;

    QJsonObject error =
        d->m_pObject->value(QLatin1String("error")).toObject();
    const QJsonValue &value = error.value(QLatin1String("code"));
    if (value.isString())
        return value.toString().toInt();
    return value.toInt();
}

QString DapRpcMessage::errorMessage() const
{
    if (d->m_type != DapRpcMessage::Error || !d->m_pObject)
        return QString();

    QJsonObject error =
        d->m_pObject->value(QLatin1String("error")).toObject();
    return error.value(QLatin1String("message")).toString();
}

QJsonValue DapRpcMessage::errorData() const
{
    if (d->m_type != DapRpcMessage::Error || !d->m_pObject)
        return QJsonValue(QJsonValue::Undefined);

    QJsonObject error =
        d->m_pObject->value(QLatin1String("error")).toObject();
    return error.value(QLatin1String("data"));
}

static QDebug operator<<(QDebug dbg, DapRpcMessage::Type type)
{
    switch (type) {
    case DapRpcMessage::Request:
        return dbg << "DapRpcMessage::Request";
    case DapRpcMessage::Response:
        return dbg << "DapRpcMessage::Response";
    case DapRpcMessage::Notification:
        return dbg << "DapRpcMessage::Notification";
    case DapRpcMessage::Error:
        return dbg << "DapRpcMessage::Error";
    default:
        return dbg << "DapRpcMessage::Invalid";
    }
}

QDebug operator<<(QDebug dbg, const DapRpcMessage &msg)
{
    dbg.nospace() << "DapRpcMessage(type=" << msg.type();
    if (msg.type() != DapRpcMessage::Notification) {
        dbg.nospace() << ", id=" << msg.id();
    }

    if (msg.type() == DapRpcMessage::Request ||
        msg.type() == DapRpcMessage::Notification) {
        dbg.nospace() << ", method=" << msg.method()
                      << ", params=" << msg.params();
    } else if (msg.type() == DapRpcMessage::Response) {
        dbg.nospace() << ", result=" << msg.toJsonValue();
    } else if (msg.type() == DapRpcMessage::Error) {
        dbg.nospace() << ", code=" << msg.errorCode()
                      << ", message=" << msg.errorMessage()
                      << ", data=" << msg.errorData();
    }
    dbg.nospace() << ")";
    return dbg.space();
}