@@ -487,6 +487,156 @@ class [[nodiscard("unnecessary construction")]] AwaitableObject
487487 std::variant<T, std::future<T>> _value;
488488};
489489
490+ // Type-erased visitor for resolvers.
491+ class [[nodiscard(" unnecessary construction" )]] ResolverVisitor final
492+ : public std::enable_shared_from_this<ResolverVisitor>
493+ {
494+ private:
495+ struct Concept
496+ {
497+ virtual ~Concept () = default ;
498+
499+ virtual void add_value (std::shared_ptr<const response::Value>&& value) = 0;
500+
501+ virtual void reserve (std::size_t count) = 0;
502+
503+ virtual void start_object () = 0;
504+ virtual void add_member (std::string&& key) = 0;
505+ virtual void end_object () = 0;
506+
507+ virtual void start_array () = 0;
508+ virtual void end_array () = 0;
509+
510+ virtual void add_null () = 0;
511+ virtual void add_string (std::string&& value) = 0;
512+ virtual void add_enum (std::string&& value) = 0;
513+ virtual void add_id (response::IdType&& value) = 0;
514+ virtual void add_bool (bool value) = 0;
515+ virtual void add_int (int value) = 0;
516+ virtual void add_float (double value) = 0;
517+
518+ virtual void add_error (schema_error&& error) = 0;
519+ };
520+
521+ template <class T >
522+ struct Model : Concept
523+ {
524+ explicit Model (std::shared_ptr<T> pimpl) noexcept
525+ : _pimpl { std::move (pimpl) }
526+ {
527+ }
528+
529+ void add_value (std::shared_ptr<const response::Value>&& value) final
530+ {
531+ _pimpl->add_value (std::move (value));
532+ }
533+
534+ void reserve (std::size_t count) final
535+ {
536+ _pimpl->reserve (count);
537+ }
538+
539+ void start_object () final
540+ {
541+ _pimpl->start_object ();
542+ }
543+
544+ void add_member (std::string&& key) final
545+ {
546+ _pimpl->add_member (std::move (key));
547+ }
548+
549+ void end_object () final
550+ {
551+ _pimpl->end_object ();
552+ }
553+
554+ void start_array () final
555+ {
556+ _pimpl->start_array ();
557+ }
558+
559+ void end_array () final
560+ {
561+ _pimpl->end_array ();
562+ }
563+
564+ void add_null () final
565+ {
566+ _pimpl->add_null ();
567+ }
568+
569+ void add_string (std::string&& value) final
570+ {
571+ _pimpl->add_string (std::move (value));
572+ }
573+
574+ void add_enum (std::string&& value) final
575+ {
576+ _pimpl->add_enum (std::move (value));
577+ }
578+
579+ void add_id (response::IdType&& value) final
580+ {
581+ _pimpl->add_id (std::move (value));
582+ }
583+
584+ void add_bool (bool value) final
585+ {
586+ _pimpl->add_bool (value);
587+ }
588+
589+ void add_int (int value) final
590+ {
591+ _pimpl->add_int (value);
592+ }
593+
594+ void add_float (double value) final
595+ {
596+ _pimpl->add_float (value);
597+ }
598+
599+ void add_error (schema_error&& error) final
600+ {
601+ _pimpl->add_error (std::move (error));
602+ }
603+
604+ private:
605+ std::shared_ptr<T> _pimpl;
606+ };
607+
608+ const std::shared_ptr<Concept> _concept;
609+
610+ public:
611+ template <class T >
612+ ResolverVisitor (std::shared_ptr<T> writer) noexcept
613+ : _concept { std::static_pointer_cast<Concept>(
614+ std::make_shared<Model<T>>(std::move (writer))) }
615+ {
616+ }
617+
618+ GRAPHQLSERVICE_EXPORT void add_value (std::shared_ptr<const response::Value>&& value);
619+
620+ GRAPHQLSERVICE_EXPORT void reserve (std::size_t count);
621+
622+ GRAPHQLSERVICE_EXPORT void start_object ();
623+ GRAPHQLSERVICE_EXPORT void add_member (std::string&& key);
624+ GRAPHQLSERVICE_EXPORT void end_object ();
625+
626+ GRAPHQLSERVICE_EXPORT void start_array ();
627+ GRAPHQLSERVICE_EXPORT void end_array ();
628+
629+ GRAPHQLSERVICE_EXPORT void add_null ();
630+ GRAPHQLSERVICE_EXPORT void add_string (std::string&& value);
631+ GRAPHQLSERVICE_EXPORT void add_enum (std::string&& value);
632+ GRAPHQLSERVICE_EXPORT void add_id (response::IdType&& value);
633+ GRAPHQLSERVICE_EXPORT void add_bool (bool value);
634+ GRAPHQLSERVICE_EXPORT void add_int (int value);
635+ GRAPHQLSERVICE_EXPORT void add_float (double value);
636+
637+ GRAPHQLSERVICE_EXPORT void add_error (schema_error&& error);
638+ };
639+
490640// Fragments are referenced by name and have a single type condition (except for inline
491641// fragments, where the type condition is common but optional). They contain a set of fields
492642// (with optional aliases and sub-selections) and potentially references to other fragments.
@@ -535,11 +685,104 @@ struct [[nodiscard("unnecessary construction")]] ResolverParams : SelectionSetPa
535685 const response::Value& variables;
536686};
537687
688+ // Pending token for ResolverVisitor.
689+ struct [[nodiscard(" unnecessary construction" )]] ResultToken
690+ {
691+ using OpaqueValue = std::shared_ptr<const response::Value>;
692+
693+ struct Reserve
694+ {
695+ std::size_t capacity;
696+ };
697+
698+ struct StartObject
699+ {
700+ };
701+
702+ struct AddMember
703+ {
704+ std::string key;
705+ };
706+
707+ struct EndObject
708+ {
709+ };
710+
711+ struct StartArray
712+ {
713+ };
714+
715+ struct EndArray
716+ {
717+ };
718+
719+ struct NullValue
720+ {
721+ };
722+
723+ struct StringValue
724+ {
725+ std::string value;
726+ };
727+
728+ struct EnumValue
729+ {
730+ std::string value;
731+ };
732+
733+ struct IdValue
734+ {
735+ response::IdType value;
736+ };
737+
738+ struct BoolValue
739+ {
740+ bool value;
741+ };
742+
743+ struct IntValue
744+ {
745+ int value;
746+ };
747+
748+ struct FloatValue
749+ {
750+ double value;
751+ };
752+
753+ GRAPHQLSERVICE_EXPORT explicit ResultToken (OpaqueValue&& value);
754+ GRAPHQLSERVICE_EXPORT explicit ResultToken (Reserve&& value);
755+ GRAPHQLSERVICE_EXPORT explicit ResultToken (StartObject&& value);
756+ GRAPHQLSERVICE_EXPORT explicit ResultToken (AddMember&& value);
757+ GRAPHQLSERVICE_EXPORT explicit ResultToken (EndObject&& value);
758+ GRAPHQLSERVICE_EXPORT explicit ResultToken (StartArray&& value);
759+ GRAPHQLSERVICE_EXPORT explicit ResultToken (EndArray&& value);
760+ GRAPHQLSERVICE_EXPORT explicit ResultToken (NullValue&& value);
761+ GRAPHQLSERVICE_EXPORT explicit ResultToken (StringValue&& value);
762+ GRAPHQLSERVICE_EXPORT explicit ResultToken (EnumValue&& value);
763+ GRAPHQLSERVICE_EXPORT explicit ResultToken (IdValue&& value);
764+ GRAPHQLSERVICE_EXPORT explicit ResultToken (BoolValue&& value);
765+ GRAPHQLSERVICE_EXPORT explicit ResultToken (IntValue&& value);
766+ GRAPHQLSERVICE_EXPORT explicit ResultToken (FloatValue&& value);
767+
768+ GRAPHQLSERVICE_EXPORT void visit (const std::shared_ptr<ResolverVisitor>& visitor) &&;
769+
770+ private:
771+ using variant_type =
772+ std::variant<OpaqueValue, Reserve, StartObject, AddMember, EndObject, StartArray, EndArray,
773+ NullValue, StringValue, EnumValue, IdValue, BoolValue, IntValue, FloatValue>;
774+
775+ variant_type _value;
776+ };
777+
538778// Propagate data and errors together without bundling them into a response::Value struct until
539779// we're ready to return from the top level Operation.
540780struct [[nodiscard(" unnecessary construction" )]] ResolverResult
541781{
542- response::Value data;
782+ GRAPHQLSERVICE_EXPORT response::Value toValue () &&;
783+ GRAPHQLSERVICE_EXPORT void visit (const std::shared_ptr<ResolverVisitor>& visitor) &&;
784+
785+ std::list<ResultToken> data {};
543786 std::list<schema_error> errors {};
544787};
545788
@@ -1019,7 +1262,7 @@ struct ModifiedResult
10191262
10201263 if (!awaitedResult)
10211264 {
1022- co_return ResolverResult {};
1265+ co_return ResolverResult { { ResultToken { ResultToken::NullValue {} } } };
10231266 }
10241267
10251268 auto modifiedResult =
@@ -1046,8 +1289,8 @@ struct ModifiedResult
10461289 if (value)
10471290 {
10481291 ModifiedResult::validateScalar<Modifier, Other...>(*value);
1049- co_return ResolverResult { response::Value {
1050- std::shared_ptr { std::move (value) } } };
1292+ co_return ResolverResult { { ResultToken {
1293+ ResultToken::OpaqueValue { std::shared_ptr { std::move (value) } } } } };
10511294 }
10521295 }
10531296
@@ -1060,7 +1303,7 @@ struct ModifiedResult
10601303
10611304 if (!awaitedResult)
10621305 {
1063- co_return ResolverResult {};
1306+ co_return ResolverResult { { ResultToken { ResultToken::NullValue {} } } };
10641307 }
10651308
10661309 auto modifiedResult = co_await ModifiedResult::convert<Other...>(std::move (*awaitedResult),
@@ -1083,8 +1326,8 @@ struct ModifiedResult
10831326 if (value)
10841327 {
10851328 ModifiedResult::validateScalar<Modifier, Other...>(*value);
1086- co_return ResolverResult { response::Value {
1087- std::shared_ptr { std::move (value) } } };
1329+ co_return ResolverResult { { ResultToken {
1330+ ResultToken::OpaqueValue { std::shared_ptr { std::move (value) } } } } };
10881331 }
10891332 }
10901333
@@ -1128,9 +1371,10 @@ struct ModifiedResult
11281371 }
11291372 }
11301373
1131- ResolverResult document { response::Value { response::Type::List } } ;
1374+ ResolverResult document;
11321375
1133- document.data .reserve (children.size ());
1376+ document.data .push_back (ResultToken { ResultToken::StartArray {} });
1377+ document.data .push_back (ResultToken { ResultToken::Reserve { children.size () } });
11341378 std::get<std::size_t >(params.errorPath ->segment ) = 0 ;
11351379
11361380 for (auto & child : children)
@@ -1141,11 +1385,11 @@ struct ModifiedResult
11411385
11421386 auto value = co_await std::move (child);
11431387
1144- document.data .emplace_back ( std::move (value.data ));
1388+ document.data .splice (document. data . end (), std::move (value.data ));
11451389
11461390 if (!value.errors .empty ())
11471391 {
1148- document.errors .splice (document.errors .end (), value.errors );
1392+ document.errors .splice (document.errors .end (), std::move ( value.errors ) );
11491393 }
11501394 }
11511395 catch (schema_exception& scx)
@@ -1171,6 +1415,8 @@ struct ModifiedResult
11711415 ++std::get<std::size_t >(params.errorPath ->segment );
11721416 }
11731417
1418+ document.data .push_back (ResultToken { ResultToken::EndArray {} });
1419+
11741420 co_return document;
11751421 }
11761422
@@ -1213,7 +1459,7 @@ struct ModifiedResult
12131459 }
12141460
12151461 using ResolverCallback =
1216- std::function<response::Value (typename ResultTraits<Type>::type, const ResolverParams&)>;
1462+ std::function<ResolverResult (typename ResultTraits<Type>::type, const ResolverParams&)>;
12171463
12181464 [[nodiscard(" unnecessary call" )]] static AwaitableResolver resolve (
12191465 typename ResultTraits<Type>::future_type result, ResolverParams&& paramsArg,
@@ -1226,7 +1472,8 @@ struct ModifiedResult
12261472 if (value)
12271473 {
12281474 Result<Type>::validateScalar (*value);
1229- co_return ResolverResult { response::Value { std::shared_ptr { std::move (value) } } };
1475+ co_return ResolverResult { { ResultToken {
1476+ ResultToken::OpaqueValue { std::shared_ptr { std::move (value) } } } } };
12301477 }
12311478
12321479 auto pendingResolver = std::move (resolver);
@@ -1238,7 +1485,14 @@ struct ModifiedResult
12381485 try
12391486 {
12401487 co_await params.launch ;
1241- document.data = pendingResolver (co_await result, params);
1488+ auto value = pendingResolver (co_await result, params);
1489+
1490+ document.data .splice (document.data .end (), std::move (value.data ));
1491+
1492+ if (!value.errors .empty ())
1493+ {
1494+ document.errors .splice (document.errors .end (), std::move (value.errors ));
1495+ }
12421496 }
12431497 catch (schema_exception& scx)
12441498 {
0 commit comments