blob: e9b72b66c569ef6266258b251f8d4e10cf137621 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "peridot/lib/fidl/json_xdr.h"
#include <string>
#include <lib/fidl/cpp/string.h>
#include <lib/fxl/macros.h>
#include "peridot/lib/rapidjson/rapidjson.h"
namespace modular {
namespace {
const char* JsonTypeName(const rapidjson::Type type) {
switch (type) {
case rapidjson::kNullType:
return "null";
case rapidjson::kFalseType:
return "false";
case rapidjson::kTrueType:
return "true";
case rapidjson::kObjectType:
return "object";
case rapidjson::kArrayType:
return "array";
case rapidjson::kStringType:
return "string";
case rapidjson::kNumberType:
return "number";
};
}
} // namespace
// HACK(mesch): We should not need this, get rid of it.
thread_local JsonValue XdrContext::null_ = JsonValue();
XdrContext::XdrContext(const XdrOp op, JsonDoc* const doc,
std::string* const error)
: parent_(nullptr),
name_(nullptr),
error_(error),
op_(op),
doc_(doc),
value_(doc) {
FXL_DCHECK(doc_ != nullptr);
FXL_DCHECK(error_ != nullptr);
}
XdrContext::XdrContext(XdrContext* const parent, const char* const name,
const XdrOp op, JsonDoc* const doc,
JsonValue* const value)
: parent_(parent),
name_(name),
error_(nullptr),
op_(op),
doc_(doc),
value_(value) {
FXL_DCHECK(parent_ != nullptr);
FXL_DCHECK(doc_ != nullptr);
FXL_DCHECK(value_ != nullptr);
}
XdrContext::~XdrContext() = default;
bool XdrContext::Version(uint32_t version) {
constexpr char kVersion[] = "@version";
switch (op_) {
case XdrOp::TO_JSON:
Field(kVersion).Value(&version);
return true;
case XdrOp::FROM_JSON: {
if (!value_->IsObject()) {
AddError("Version(): must be on an Object.");
return false;
}
auto i = value_->FindMember(kVersion);
if (i == value_->MemberEnd()) {
AddError("Version(): No @version present.");
return false;
}
uint32_t actual_version{};
Field(kVersion).Value(&actual_version);
if (actual_version != version) {
AddError("Version(): Found version " + std::to_string(actual_version) +
" but expected version " + std::to_string(version));
return false;
}
return true;
}
}
}
void XdrContext::Value(unsigned char* const data) {
switch (op_) {
case XdrOp::TO_JSON:
value_->Set(static_cast<int>(*data), allocator());
break;
case XdrOp::FROM_JSON:
if (!value_->Is<int>()) {
AddError("Value() of unsigned char: int expected");
return;
}
*data = static_cast<unsigned char>(value_->Get<int>());
}
}
void XdrContext::Value(int8_t* const data) {
switch (op_) {
case XdrOp::TO_JSON:
value_->Set(static_cast<int>(*data), allocator());
break;
case XdrOp::FROM_JSON:
if (!value_->Is<int>()) {
AddError("Value() of int8: int expected");
return;
}
*data = static_cast<int8_t>(value_->Get<int>());
}
}
void XdrContext::Value(unsigned short* const data) {
switch (op_) {
case XdrOp::TO_JSON:
value_->Set(static_cast<int>(*data), allocator());
break;
case XdrOp::FROM_JSON:
if (!value_->Is<int>()) {
AddError("Value() of unsigned short: int expected");
return;
}
*data = static_cast<unsigned short>(value_->Get<int>());
}
}
void XdrContext::Value(short* const data) {
switch (op_) {
case XdrOp::TO_JSON:
value_->Set(static_cast<int>(*data), allocator());
break;
case XdrOp::FROM_JSON:
if (!value_->Is<int>()) {
AddError("Value() of short: int expected");
return;
}
*data = static_cast<short>(value_->Get<int>());
}
}
void XdrContext::Value(fidl::StringPtr* const data) {
switch (op_) {
case XdrOp::TO_JSON:
if (data->is_null()) {
value_->SetNull();
} else {
value_->SetString(data->get(), allocator());
}
break;
case XdrOp::FROM_JSON:
if (value_->IsNull()) {
data->reset();
} else if (value_->IsString()) {
*data = value_->GetString();
} else {
AddError("Value() of fidl::StringPtr: string expected");
}
break;
}
}
void XdrContext::Value(std::string* const data) {
switch (op_) {
case XdrOp::TO_JSON:
value_->SetString(*data, allocator());
break;
case XdrOp::FROM_JSON:
if (value_->IsString()) {
*data = value_->GetString();
} else {
AddError("Value() of std::string: string expected");
}
break;
}
}
XdrContext XdrContext::Field(const char field[]) {
switch (op_) {
case XdrOp::TO_JSON:
if (!value_->IsObject()) {
value_->SetObject();
}
break;
case XdrOp::FROM_JSON:
if (!value_->IsObject()) {
AddError("Object expected for field " + std::string(field));
return {this, field, op_, doc_, &null_};
}
}
auto i = value_->FindMember(field);
if (i != value_->MemberEnd()) {
return {this, field, op_, doc_, &i->value};
}
switch (op_) {
case XdrOp::TO_JSON: {
JsonValue name{field, allocator()};
value_->AddMember(name, JsonValue(), allocator());
auto i = value_->FindMember(field);
FXL_DCHECK(i != value_->MemberEnd());
return {this, field, op_, doc_, &i->value};
}
case XdrOp::FROM_JSON:
return {this, field, op_, doc_, &null_};
}
}
XdrContext XdrContext::Element(const size_t i) {
switch (op_) {
case XdrOp::TO_JSON:
if (!value_->IsArray()) {
value_->SetArray();
}
break;
case XdrOp::FROM_JSON:
if (!value_->IsArray()) {
AddError("Array expected for element " + std::to_string(i));
return {this, nullptr, op_, doc_, &null_};
}
}
if (i < value_->Size()) {
return {this, nullptr, op_, doc_, &value_->operator[](i)};
}
switch (op_) {
case XdrOp::TO_JSON:
while (i >= value_->Size()) {
value_->PushBack(JsonValue(), allocator());
}
return {this, nullptr, op_, doc_, &value_->operator[](i)};
case XdrOp::FROM_JSON:
return {this, nullptr, op_, doc_, &null_};
}
}
void XdrContext::AddError(const std::string& message) {
auto error = AddError();
error->append(": " + message + "\n");
}
std::string* XdrContext::AddError() {
std::string* const ret = parent_ ? parent_->AddError() : error_;
if (parent_) {
ret->append("/");
}
ret->append(JsonTypeName(value_->GetType()));
if (name_) {
ret->append(" ");
ret->append(name_);
}
return ret;
}
std::string* XdrContext::GetError() {
return parent_ ? parent_->GetError() : error_;
}
} // namespace modular