diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 270b37f75..dfe20a2e6 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -190,9 +190,11 @@ namespace diff{ template X const& get() const; + /** determine if payload constitutes a nested scope ("object") */ + bool isNested() const; + /** peek into the type field of a nested `Record` */ - string - recordType() const; + string recordType() const; /** visit _children_ of a nested `Record` */ Rec::scopeIter @@ -712,6 +714,12 @@ namespace diff{ : util::BOTTOM_INDICATOR; } + inline bool + DataCap::isNested() const + { + return util::BOTTOM_INDICATOR != recordType(); + } + diff --git a/src/lib/diff/tree-mutator-collection-binding.hpp b/src/lib/diff/tree-mutator-collection-binding.hpp index 1b2b696b7..a8041e169 100644 --- a/src/lib/diff/tree-mutator-collection-binding.hpp +++ b/src/lib/diff/tree-mutator-collection-binding.hpp @@ -76,11 +76,11 @@ * an otherwise opaque implementation data structure. * * @tparam COLL a STL compliant collection type holding "child elements" - * @tparam MAT a closure to determine if a child matches a diff spec (GenNode) - * @tparam CTR a closure to construct a new child element from a given diff spec + * @tparam MAT a functor to determine if a child matches a diff spec (GenNode) + * @tparam CTR a functor to construct a new child element from a given diff spec * @tparam SEL predicate to determine if this binding layer has to process a diff message - * @tparam ASS a closure to assign / set a new value from a given diff spec - * @tparam MUT a closure to construct a nested mutator for some child element + * @tparam ASS a functor to assign / set a new value from a given diff spec + * @tparam MUT a functor to construct a nested mutator for some child element */ template struct CollectionBinding @@ -436,42 +436,70 @@ }; - using lib::meta::enable_if; - using lib::diff::can_wrap_in_GenNode; + /** builder function to synthesise builder type from given functors */ + template + inline auto + createCollectionBindingBuilder (COLL& coll, MAT m, CTR c, SEL s, ASS a, MUT u) + { + using Coll = typename Strip::TypeReferred; + + return CollectionBindingBuilder {coll, m,c,s,a,u}; + } - template - struct _DefaultPayload + template + struct _EmptyBinding { static bool - match (GenNode const&, ELM const&) + __ERROR_missing_matcher (GenNode const&, ELM const&) { throw error::Logic ("unable to build a sensible default matching predicate"); } static ELM - construct (GenNode const&) + __ERROR_missing_constructor (GenNode const&) { throw error::Logic ("unable to build a sensible default for creating new elements"); } - }; - - template - struct _DefaultPayload>> - { + static bool - match (GenNode const& spec, ELM const& elm) + ignore_selector (GenNode const&) { - return spec.matches(elm); + return true; // by default apply diff unconditionally } - static ELM - construct (GenNode const& spec) + static bool + disable_assignment (ELM&, GenNode const&) { - return spec.data.get(); + return false; + } + + static bool + disable_childMutation (ELM&, GenNode::ID const&, TreeMutator::Handle) + { + return false; + } + + + template + static auto + attachTo (COLL& coll) + { + return createCollectionBindingBuilder (coll + ,__ERROR_missing_matcher + ,__ERROR_missing_constructor + ,ignore_selector + ,disable_assignment + ,disable_childMutation + ); } }; + + + using lib::meta::enable_if; + using lib::diff::can_wrap_in_GenNode; + /** * starting point for configuration of a binding to STL container. * When using the "nested DSL" to setup a binding to child elements @@ -482,56 +510,74 @@ * the created (\ref CollectionBindingBuilder) to replace * those defaults with lambdas tied into the actual * implementation of the target data structure. + * @note depending on the payload type within the collection, + * we provide some preconfigured default specialisations */ - template + template struct _DefaultBinding + : _EmptyBinding + { }; + + template + struct _DefaultBinding>> { - using Coll = typename Strip::TypeReferred; - using Elm = typename Coll::value_type; - - using Payload = _DefaultPayload; - - static bool - ignore_selector (GenNode const&) + template + static auto + attachTo (COLL& coll) { - return true; // by default apply diff unconditionally - } - - static bool - disable_assignment (Elm&, GenNode const&) - { - return false; - } - - static bool - disable_childMutation (Elm&, GenNode::ID const&, TreeMutator::Handle) - { - return false; - } - - - using FallbackBindingConfiguration - = CollectionBindingBuilder; - - static FallbackBindingConfiguration - attachTo (Coll& coll) - { - return FallbackBindingConfiguration{ coll - , Payload::match - , Payload::construct - , ignore_selector - , disable_assignment - , disable_childMutation - }; + return _EmptyBinding::attachTo(coll) + .matchElement([](GenNode const& spec, ELM const& elm) + { + return spec.matches(elm); + }) + .constructFrom([](GenNode const& spec) -> ELM + { + return spec.data.get(); + }); } }; + template<> + struct _DefaultBinding + { + template + static auto + attachTo (COLL& coll) + { + return _EmptyBinding::attachTo(coll) + .matchElement([](GenNode const& spec, GenNode const& elm) + { + return spec.matches(elm); + }) + .constructFrom([](GenNode const& spec) -> GenNode + { + return GenNode{spec}; + }) + .assignElement ([](GenNode& target, GenNode const& spec) -> bool + { + target.data = spec.data; + return true; + }) + .buildChildMutator ([](GenNode& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool + { + if (target.idi == subID // require match on already existing child object + and target.data.isNested()) + { + Rec& nestedScope = target.data.get(); + buff.create ( + TreeMutator::build() + .attach (mutateInPlace (nestedScope))); + return true; + } + else + return false; + }); + } + }; + + + + /** * Entry point to a nested DSL @@ -544,9 +590,11 @@ */ template auto - collection (COLL& coll) -> decltype(_DefaultBinding::attachTo(coll)) + collection (COLL& coll) { - return _DefaultBinding::attachTo(coll); + using Elm = typename COLL::value_type; + + return _DefaultBinding::attachTo(coll); } diff --git a/src/lib/diff/tree-mutator-gen-node-binding.hpp b/src/lib/diff/tree-mutator-gen-node-binding.hpp index bbe737abb..0fa172cb6 100644 --- a/src/lib/diff/tree-mutator-gen-node-binding.hpp +++ b/src/lib/diff/tree-mutator-gen-node-binding.hpp @@ -66,7 +66,7 @@ - using lib::diff::GenNode; + using Storage = RecordSetup::Storage; @@ -96,7 +96,11 @@ { return builderBase .attach (collection (accessChildren(targetTree))) - .attach (collection (accessAttribs(targetTree))); + .attach (collection (accessAttribs(targetTree))) + .isApplicableIf ([&](GenNode const& spec) + { + return spec.isNamed(); // »Selector« : treat key-value elements here + }); } diff --git a/tests/library/diff/diff-complex-application-test.cpp b/tests/library/diff/diff-complex-application-test.cpp index 67fb80ff6..dae317bd3 100644 --- a/tests/library/diff/diff-complex-application-test.cpp +++ b/tests/library/diff/diff-complex-application-test.cpp @@ -213,7 +213,7 @@ namespace test{ .attach (collection(nestedObj_) .isApplicableIf ([&](GenNode const& spec) -> bool { - return BOTTOM_INDICATOR != spec.data.recordType(); // »Selector« : require object-like sub scope + return spec.data.isNested(); // »Selector« : require object-like sub scope }) .matchElement ([&](GenNode const& spec, Opaque const& elm) -> bool {