| // 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; |
| |
| 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_; |
| } |
| |
| XdrContext::XdrCallbackOnReadError XdrContext::ReadErrorHandler( |
| std::function<void()> callback) { |
| return XdrContext::XdrCallbackOnReadError(this, op_, GetError(), |
| std::move(callback)); |
| } |
| |
| XdrContext::XdrCallbackOnReadError::XdrCallbackOnReadError( |
| XdrContext* context, |
| XdrOp op, |
| std::string* error, |
| std::function<void()> callback) |
| : context_(context), |
| op_(op), |
| error_(error), |
| old_length_(error->size()), |
| error_callback_(std::move(callback)) {} |
| |
| XdrContext::XdrCallbackOnReadError::XdrCallbackOnReadError( |
| XdrCallbackOnReadError&& rhs) |
| : context_(rhs.context_), |
| op_(rhs.op_), |
| error_(rhs.error_), |
| old_length_(rhs.old_length_), |
| error_callback_(std::move(rhs.error_callback_)) {} |
| |
| XdrContext::XdrCallbackOnReadError::~XdrCallbackOnReadError() { |
| if (error_->size() != old_length_ && op_ == XdrOp::FROM_JSON) { |
| error_->resize(old_length_); |
| error_callback_(); |
| } |
| } |
| |
| } // namespace modular |