Skip to content

Commit 9505d83

Browse files
committed
feat: deferred ResolverVisitor
1 parent 44bc921 commit 9505d83

File tree

10 files changed

+702
-103
lines changed

10 files changed

+702
-103
lines changed

include/graphqlservice/GraphQLService.h

Lines changed: 268 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
540780
struct [[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
{

include/graphqlservice/Service.ixx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,13 @@ using service::FieldParams;
6666
using service::AwaitableScalar;
6767
using service::AwaitableObject;
6868

69+
using service::ResolverVisitor;
70+
6971
using service::Fragment;
7072
using service::FragmentMap;
7173

7274
using service::ResolverParams;
75+
using service::ResultToken;
7376
using service::ResolverResult;
7477
using service::AwaitableResolver;
7578
using service::Resolver;

0 commit comments

Comments
 (0)