/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace facebook { namespace thrift { namespace detail { template struct Ensure { constexpr decltype(auto) operator()(Id id, T& obj) const { return apache::thrift::op::ensure<>(id, obj); } }; // the shared pointer to const object needs to be recreated as a whole to // obtain a mutable reference to its object template struct Ensure> { constexpr R& operator()(Id, T& obj) const { std::shared_ptr& s = apache::thrift::op::get(obj); auto temp = std::make_shared(); s = temp; return *temp; } }; template static constexpr decltype(auto) ensure(Id id, T& obj) { return Ensure>{}(id, obj); } template struct dynamic_converter_impl> { static void to(folly::dynamic& out, T const& input, dynamic_format format) { switch (format) { case dynamic_format::PORTABLE: { const auto s = apache::thrift::TEnumTraits::findName(input); if (!s) { throw std::invalid_argument("invalid enum value"); } out = s; break; } case dynamic_format::JSON_1: out = folly::to_underlying(input); break; default: assert("to_dynamic: unsupported format" == nullptr); break; } } static void from_portable(T& out, const folly::dynamic& input) { const auto& value = input.asString(); if (!apache::thrift::TEnumTraits::findValue(value.c_str(), &out)) { throw std::invalid_argument("unrecognized enum value"); } } static void from_json_1(T& out, const folly::dynamic& input) { out = static_cast(input.asInt()); } static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { switch (adherence) { case format_adherence::STRICT: switch (format) { case dynamic_format::PORTABLE: from_portable(out, input); break; case dynamic_format::JSON_1: from_json_1(out, input); break; default: assert("from_dynamic (STRICT): unsupported format" == nullptr); break; } break; case format_adherence::LENIENT: switch (format) { case dynamic_format::PORTABLE: case dynamic_format::JSON_1: if (input.isInt()) { from_json_1(out, input); } else { from_portable(out, input); } break; default: assert("from_dynamic (LENIENT): unsupported format" == nullptr); break; } break; default: assert("from_dynamic: unsupported format adherence" == nullptr); break; } } }; template struct dynamic_converter_impl> { template static auto do_reserve(T& c, size_t size) -> decltype(c.reserve(size)) { c.reserve(size); } template static void do_reserve(T&, ...) {} template static void to(folly::dynamic& out, T const& input, dynamic_format format) { out = folly::dynamic::array; for (const auto& i : input) { folly::dynamic value(folly::dynamic::object); dynamic_converter_impl::to(value, i, format); out.push_back(std::move(value)); } } template static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.empty()) { return; } do_reserve(out, input.size()); for (const auto& i : input) { out.emplace_back(); dynamic_converter_impl::from(out.back(), i, format, adherence); } } }; template struct dynamic_converter_impl> { template static void to(folly::dynamic& out, T const& input, dynamic_format format) { out = folly::dynamic::object; for (const auto& [k, m] : input) { folly::dynamic key(folly::dynamic::object); dynamic_converter_impl::to(key, k, format); dynamic_converter_impl::to(out[std::move(key)], m, format); } } template static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.empty()) { return; } for (const auto& [name, entry] : input.items()) { typename T::key_type key; dynamic_converter_impl::from(key, name, format, adherence); dynamic_converter_impl::from( out[std::move(key)], entry, format, adherence); } } }; template struct dynamic_converter_impl> { template static void to(folly::dynamic& out, T const& input, dynamic_format format) { out = folly::dynamic::array; for (const auto& i : input) { folly::dynamic value(folly::dynamic::object); dynamic_converter_impl::to(value, i, format); out.push_back(std::move(value)); } } template static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.empty()) { return; } for (const auto& i : input) { typename T::value_type value; dynamic_converter_impl::from(value, i, format, adherence); out.insert(std::move(value)); } } }; template struct dynamic_converter_impl> { static void to(folly::dynamic& out, T const& input, dynamic_format format) { out = folly::dynamic::object; apache::thrift::op::invoke_by_field_id( static_cast(input.getType()), [&](auto id) { using Id = decltype(id); using FieldTag = apache::thrift::op::get_field_tag; dynamic_converter_impl::to(out, input, format); }, [] { // union is __EMPTY__ }); } static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (!input.isObject()) { throw std::invalid_argument(folly::to( "dynamic input when converting to variant must be object: ", input.typeName())); } else if (input.size() > 1) { throw std::invalid_argument("unexpected additional fields for a variant"); } if (input.empty()) { apache::thrift::op::clear<>(out); return; } const auto& [name, entry] = *input.items().begin(); const bool found = apache::thrift::op::find_by_field_id( [&, &name = name, &entry = entry](auto id) { using Id = decltype(id); using FieldTag = apache::thrift::op::get_field_tag; if (apache::thrift::op::get_name_v == name.stringPiece()) { if (!entry.isNull()) { dynamic_converter_impl::from( out, entry, format, adherence); } return true; } return false; }); if (!found) { throw std::invalid_argument("unrecognized variant type"); } } }; template struct dynamic_converter_impl> { static void to(folly::dynamic& out, T const& input, dynamic_format format) { out = folly::dynamic::object; apache::thrift::op::for_each_field_id([&](auto id) { using Id = decltype(id); using FieldTag = apache::thrift::op::get_field_tag; dynamic_converter_impl::to(out, input, format); }); } static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { apache::thrift::op::for_each_field_id([&](auto id) { using Id = decltype(id); using FieldTag = apache::thrift::op::get_field_tag; if (auto it = input.find(apache::thrift::op::get_name_v); it != input.items().end()) { const auto& [_, entry] = *it; if (!entry.isNull()) { dynamic_converter_impl::from(out, entry, format, adherence); } } }); } }; template struct dynamic_converter_impl_field { static void to( folly::dynamic& out, Struct const& input, dynamic_format format) { using Id = apache::thrift::field_id; folly::StringPiece fieldName = apache::thrift::op::get_name_v; if (const auto* ref = apache::thrift::op::getValueOrNull( apache::thrift::op::get(input))) { dynamic_converter_impl::to(out[fieldName], *ref, format); } else if (!apache::thrift::detail::is_optional_or_union_field_ref_v< apache::thrift::op::get_field_ref>) { out[fieldName] = nullptr; } } }; template struct dynamic_converter_impl>> { static void to( folly::dynamic& out, Struct const& input, dynamic_format format) { dynamic_converter_impl_field::to(out, input, format); } static void from( Struct& s, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { dynamic_converter_impl::from( ensure<>(id, s), input, format, adherence); } private: using Id = apache::thrift::field_id; static constexpr auto id = Id{}; }; template struct dynamic_converter_impl, apache::thrift::FieldContext>> { static void to( folly::dynamic& out, Struct const& input, dynamic_format format) { dynamic_converter_impl_field< apache::thrift::type::adapted, Struct, FieldId>::to(out, input, format); } static void from( Struct& s, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { using Id = apache::thrift::field_id; using FieldType = apache::thrift::op::get_native_type; std::remove_cvref_t< apache::thrift::adapt_detail::thrift_t> temp; dynamic_converter_impl::from(temp, input, format, adherence); apache::thrift::op::get(s) = apache::thrift::adapt_detail::fromThriftField( std::move(temp), s); } }; template struct dynamic_converter_impl> { template static void to(folly::dynamic& out, T const& input, dynamic_format format) { dynamic_converter_impl::to(out, Adapter::toThrift(input), format); } template static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { std::remove_cvref_t> temp; dynamic_converter_impl::from(temp, input, format, adherence); out = Adapter::fromThrift(std::move(temp)); } }; template <> struct dynamic_converter_impl { template static void to(folly::dynamic& out, T const& input, dynamic_format) { out = input; } static void from( std::string& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = input.asString(); } template static void from( T& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = input.asString(); } }; template <> struct dynamic_converter_impl { template static void to(folly::dynamic& out, T const& input, dynamic_format) { out = folly::to(input); } static void to( folly::dynamic& out, std::unique_ptr const& input, dynamic_format format) { if (input) { to(out, *input, format); } else { out = nullptr; } } static void to( folly::dynamic& out, folly::IOBuf const& input, dynamic_format) { folly::IOBufQueue q; q.append(input); std::string str; q.appendToString(str); out = std::move(str); } static void from( std::string& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = input.asString(); } static void from( std::unique_ptr& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = folly::IOBuf::copyBuffer(input.asString()); } static void from( folly::IOBuf& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = folly::IOBuf(folly::IOBuf::CopyBufferOp::COPY_BUFFER, input.asString()); } template static void from( T& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = input.asString(); } }; template struct dynamic_converter_impl_floating_point { template static void to(folly::dynamic& out, T const& input, dynamic_format) { out = static_cast(input); } template static void from( T& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = static_cast(input.asDouble()); } }; template <> struct dynamic_converter_impl : dynamic_converter_impl_floating_point {}; template <> struct dynamic_converter_impl : dynamic_converter_impl_floating_point {}; template <> struct dynamic_converter_impl { static void to(folly::dynamic& out, bool input, dynamic_format) { out = input; } static void from( bool& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = input.asBool(); } static void from( typename std::vector::reference out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { bool tmp; from(tmp, input, format, adherence); out = tmp; } }; template struct dynamic_converter_impl_integral { template static void to(folly::dynamic& out, T const& input, dynamic_format) { out = input; } template static void from( T& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = static_cast(input.asInt()); } }; template <> struct dynamic_converter_impl : dynamic_converter_impl_integral {}; template <> struct dynamic_converter_impl : dynamic_converter_impl_integral {}; template <> struct dynamic_converter_impl : dynamic_converter_impl_integral {}; template <> struct dynamic_converter_impl : dynamic_converter_impl_integral {}; template struct dynamic_converter_impl> { static void to(folly::dynamic& out, T const& input, dynamic_format format) { dynamic_converter_impl::to(out, input, format); } static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { dynamic_converter_impl::from(out, input, format, adherence); } }; } // namespace detail } // namespace thrift } // namespace facebook