/* * 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. */ #ifndef THRIFT_FATAL_FOLLY_DYNAMIC_INL_POST_H_ #define THRIFT_FATAL_FOLLY_DYNAMIC_INL_POST_H_ 1 #include #include #include #include #include #include #include #include namespace apache { namespace thrift { namespace detail { struct recurse_helper { /* * This implicitly will dereference any smart pointer before hitting * real conversion logic as we recurse. This is mainly to support * structs that use cpp.ref or cpp.ref_type annotations. */ template static void to(folly::dynamic& out, T const& input, dynamic_format format) { dynamic_converter_impl::to(out, input, format); } template static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { dynamic_converter_impl::from(out, input, format, adherence); } template static void to( folly::dynamic& out, optional_boxed_field_ref&> input, dynamic_format format) { if (!input) { out = nullptr; return; } dynamic_converter_impl::to(out, *input, format); } template static void from( optional_boxed_field_ref&> out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.isNull()) { out.reset(); return; } dynamic_converter_impl::from(out.emplace(), input, format, adherence); } template static void to( folly::dynamic& out, const std::shared_ptr& input, dynamic_format format) { if (!input) { out = nullptr; return; } dynamic_converter_impl::to(out, *input, format); } template static void from( std::shared_ptr& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.isNull()) { out.reset(); return; } auto temp = std::make_shared(); dynamic_converter_impl::from(*temp, input, format, adherence); out = std::move(temp); } template static void to( folly::dynamic& out, const std::unique_ptr& input, dynamic_format format) { if (!input) { out = nullptr; return; } dynamic_converter_impl::to(out, *input, format); } template static void from( std::unique_ptr& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.isNull()) { out.reset(); return; } auto temp = std::make_unique(); dynamic_converter_impl::from(*temp, input, format, adherence); out = std::move(temp); } template static void to( folly::dynamic& out, const std::shared_ptr& input, dynamic_format format) { if (!input) { out = nullptr; return; } dynamic_converter_impl::to(out, *input, format); } template static void from( std::shared_ptr& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.isNull()) { out.reset(); return; } auto temp = std::make_shared(); dynamic_converter_impl::from(*temp, input, format, adherence); out = std::move(temp); } template static void from( std::unique_ptr& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { if (input.isNull()) { out.reset(); return; } // explicit overload exists for IOBuf -> binary so we add here too dynamic_converter_impl::from(out, input, format, adherence); } template static void from( typename std::vector::reference out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { bool tmp; dynamic_converter_impl::from(tmp, input, format, adherence); out = tmp; } }; template <> struct dynamic_converter_impl { template static void to(folly::dynamic& out, T const& input, dynamic_format format) { switch (format) { case dynamic_format::PORTABLE: { const auto s = TEnumTraits::findName(input); if (!s) { throw std::invalid_argument("invalid enum value"); } out = s; break; } case dynamic_format::JSON_1: out = static_cast::int_type>(input); break; default: assert("to_dynamic: unsupported format" == nullptr); break; } } template static void from_portable(T& out, const folly::dynamic& input) { const auto& value = input.asString(); if (!TEnumTraits::findValue(value.c_str(), &out)) { throw std::invalid_argument("unrecognized enum value"); } } template static void from_json_1(T& out, const folly::dynamic& input) { out = static_cast(input.asInt()); } template 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); recurse_helper::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(); recurse_helper::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); recurse_helper::to(key, k, format); recurse_helper::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& i : input.items()) { typename T::key_type key; recurse_helper::from(key, i.first, format, adherence); recurse_helper::from( out[std::move(key)], i.second, 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); recurse_helper::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; recurse_helper::from(value, i, format, adherence); out.insert(std::move(value)); } } }; template <> struct dynamic_converter_impl { template static void to(folly::dynamic& out, T const& input, dynamic_format format) { using descriptors = typename fatal::variant_traits::descriptors; out = folly::dynamic::object; fatal::scalar_search( input.getType(), [&](auto indexed) { using descriptor = decltype(fatal::tag_type(indexed)); recurse_helper::to( out[fatal::enum_to_string(input.getType(), nullptr)], typename descriptor::getter()(input), format); }); } template static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { using variant_traits = fatal::variant_traits; using id_traits = fatal::enum_traits; 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"); } auto items = input.items(); auto i = items.begin(); if (i == items.end()) { variant_traits::clear(out); } else { const auto type = i->first.stringPiece(); const auto& entry = i->second; const bool found = fatal::trie_find( type.begin(), type.end(), [&](auto tag) { using field = decltype(fatal::tag_type(tag)); using id = typename field::value; using descriptor = typename variant_traits::by_id::template descriptor; variant_traits::by_id::template set(out); recurse_helper::from( variant_traits::by_id::template get(out), entry, format, adherence); }); if (!found) { throw std::invalid_argument("unrecognized variant type"); } } } }; template <> struct dynamic_converter_impl { template static void to(folly::dynamic& out, T const& input, dynamic_format format) { out = folly::dynamic::object; fatal::foreach::members>([&](auto indexed) { using member = decltype(fatal::tag_type(indexed)); using impl = dynamic_converter_impl; static_assert( fatal::is_complete::value, "to_dynamic: unsupported type"); if (member::optional::value == optionality::optional && !member::is_set(input)) { return; } recurse_helper::to( out[folly::StringPiece( fatal::z_data(), fatal::size::value)], typename member::getter{}(input), format); }); } template static void from( T& out, const folly::dynamic& input, dynamic_format format, format_adherence adherence) { using members = typename reflect_struct::members; for (const auto& i : input.items()) { const auto member_name = i.first.stringPiece(); fatal::trie_find( member_name.begin(), member_name.end(), [&](auto tag) { using member = decltype(fatal::tag_type(tag)); member::mark_set(out, true); recurse_helper::from( typename member::getter{}(out), i.second, format, adherence); }); } } }; 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, 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 { 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 { template static void to(folly::dynamic& out, T const& input, dynamic_format) { out = input; } static void from( bool& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = input.asBool(); } template static void from( T& out, const folly::dynamic& input, dynamic_format, format_adherence) { out = static_cast(input.asInt()); } }; } // namespace detail } // namespace thrift } // namespace apache #endif // THRIFT_FATAL_FOLLY_DYNAMIC_INL_POST_H_