/* * 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 apache { namespace thrift { namespace ident { struct patch; } namespace op { class bad_patch_access : public std::runtime_error { public: bad_patch_access() noexcept : std::runtime_error("Patch guarantees value is unset.") {} }; namespace detail { /// Base class for all patch types. /// - Patch: The Thrift struct representation for the patch. /// - Derived: The leaf type deriving from this class. template class BasePatch : public type::detail::EqWrap> { using Base = type::detail::EqWrap; public: using Base::Base; BasePatch(const BasePatch&) = default; BasePatch(BasePatch&&) noexcept = default; BasePatch& operator=(const BasePatch&) = default; BasePatch& operator=(BasePatch&&) noexcept = default; /// Applies patches to a Thrift value. // Automatically dereference non-optional fields. template void apply(field_ref field) const { derived().apply(*field); } /// Applies patches to a Thrift value. template void apply(terse_field_ref field) const { derived().apply(*field); } /// Applies patches to a Thrift value. template void apply(required_field_ref field) const { derived().apply(*field); } /// Replaces the existing value. template type::if_not_id assign(field_ref val) { derived().assign(std::forward(*val)); } /// Replaces the existing value. template type::if_not_id assign(terse_field_ref val) { derived().assign(std::forward(*val)); } /// Same as `assign(...)` method. template Derived& operator=(field_ref field) { derived().assign(std::forward(*field)); return derived(); } /// Same as `assign(...)` method. template Derived& operator=(terse_field_ref field) { derived().assign(std::forward(*field)); return derived(); } /// Merges another patch into the current patch. After the merge /// (`patch.merge(next)`), `patch.apply(value)` is equivalent to /// `next.apply(patch.apply(value))`. template void merge(U&& next) { if (this == std::addressof(next)) { auto copy = next; std::forward(copy).customVisit(derived()); } else { std::forward(next).customVisit(derived()); } } [[deprecated("Use customVisit(...) method to read the patch.")]] auto&& toThrift() & { return Base::toThrift(); } [[deprecated("Use customVisit(...) method to read the patch.")]] auto&& toThrift() && { return Base::toThrift(); } [[deprecated("Use customVisit(...) method to read the patch.")]] auto&& toThrift() const& { return Base::toThrift(); } [[deprecated("Use customVisit(...) method to read the patch.")]] auto&& toThrift() const&& { return Base::toThrift(); } // Convert Static Patch to Dynamic Patch. protocol::Object toObject() const { // Pass the adapted type rather than the underlying thrift structure // directly to use `adapter::encode(...)` hook return protocol::asValueStruct(derived()).as_object(); } protected: using Base::derived; using Base::resetAnd; ~BasePatch() = default; // abstract base class }; /// Base class for assign patch types. /// /// The `Patch` template parameter must be a Thrift struct with the following /// fields: /// * `optional T assign` template class BaseAssignPatch : public BasePatch { using Base = BasePatch; public: /// The type of patched value. using value_type = folly::remove_cvref_t().assign())>; using Base::apply; using Base::assign; using Base::operator=; using Base::Base; BaseAssignPatch(const BaseAssignPatch&) = default; BaseAssignPatch(BaseAssignPatch&&) noexcept = default; BaseAssignPatch& operator=(const BaseAssignPatch&) = default; BaseAssignPatch& operator=(BaseAssignPatch&&) noexcept = default; /// Creates a new patch that replaces the existing value. template FOLLY_NODISCARD static Derived createAssign(U&& val) { Derived patch; patch.assign(std::forward(val)); return patch; } void assign(const value_type& val) { resetAnd().assign().emplace(val); } void assign(value_type&& val) { resetAnd().assign().emplace(std::move(val)); } // A 'value' patch only applies to set optional values. template type::if_optional_or_union_field_ref> apply( U&& field) const { if (field.has_value()) { derived().apply(*std::forward(field)); } } // A 'value' patch only applies to set union field values. template void apply(union_field_ref field) const { if (field.has_value()) { derived().apply(*field); } } template void apply(std::unique_ptr& field) const { if (field) { derived().apply(*field); } } Derived& operator=(const value_type& val) { return (assign(val), derived()); } Derived& operator=(value_type&& val) { assign(std::move(val)); return derived(); } protected: using Base::data_; using Base::derived; using Base::resetAnd; ~BaseAssignPatch() = default; // abstract base class FOLLY_NODISCARD bool hasAssign() const { return data_.assign().has_value(); } FOLLY_NODISCARD value_type& assignOr(value_type& value) noexcept { return hasAssign() ? *data_.assign() : value; } }; /// Base class for clearable patch types. /// /// The `Patch` template parameter must be a Thrift struct with the following /// fields: /// * `optional T assign` /// * `[terse] bool clear` template class BaseClearPatch : public BaseAssignPatch { using Base = BaseAssignPatch; using T = typename Base::value_type; public: using Base::Base; using Base::operator=; using Base::apply; BaseClearPatch(const BaseClearPatch&) = default; BaseClearPatch(BaseClearPatch&&) noexcept = default; BaseClearPatch& operator=(const BaseClearPatch&) = default; BaseClearPatch& operator=(BaseClearPatch&&) noexcept = default; /// Creates a new patch that clears the value. FOLLY_NODISCARD static Derived createClear() { Derived patch; patch.clear(); return patch; } // Clear resets optional fields. template type::if_optional_or_union_field_ref> apply( U&& field) const { if (data_.clear() == true && !hasAssign()) { field.reset(); } else if (field.has_value()) { derived().apply(*std::forward(field)); } } protected: template friend class BaseEnsurePatch; using Base::data_; using Base::derived; using Base::hasAssign; using Base::resetAnd; ~BaseClearPatch() = default; template bool customVisitAssignAndClear(Visitor&& v) const { if (hasAssign()) { std::forward(v).assign(*data_.assign()); return true; } if (data_.clear() == true) { std::forward(v).clear(); } return false; } /// Clears the value. void clear() { resetAnd().clear() = true; } }; /// Base class for 'container' patch types. /// /// The `Patch` template parameter must be a Thrift struct with the following /// * `optional T assign` /// * `[terse] bool clear` template class BaseContainerPatch : public BaseClearPatch { using Base = BaseClearPatch; public: using Base::Base; using Base::operator=; using Base::clear; protected: ~BaseContainerPatch() = default; // Abstract base class. }; } // namespace detail } // namespace op } // namespace thrift } // namespace apache