diff --git a/include/ClientGenerator.h b/include/ClientGenerator.h index 4070a12f..a6109108 100644 --- a/include/ClientGenerator.h +++ b/include/ClientGenerator.h @@ -64,6 +64,55 @@ class [[nodiscard("unnecessary construction")]] Generator const std::string& outerScope, const ResponseField& responseField) const noexcept; [[nodiscard("unnecessary memory copy")]] static std::string getTypeModifierList( const TypeModifierStack& modifiers) noexcept; + void outputResponseFieldVisitorStates(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parent = {}) const noexcept; + void outputResponseFieldVisitorAddValue(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}, std::string_view parentAccessor = {}, + std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorReserve(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState = {}, + std::string_view parentAccessor = {}, std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorStartObject(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState = {}, + std::string_view parentAccessor = {}, std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorAddMember(std::ostream& sourceFile, + const ResponseFieldList& children, bool arrayElement = false, + std::string_view parentState = {}) const noexcept; + void outputResponseFieldVisitorEndObject(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}) const noexcept; + void outputResponseFieldVisitorStartArray(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState = {}, + std::string_view parentAccessor = {}, std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorEndArray(std::ostream& sourceFilearrayElement, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}) const noexcept; + void outputResponseFieldVisitorAddNull(std::ostream& sourceFilearrayElement, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}, std::string_view parentAccessor = {}) const noexcept; + void outputResponseFieldVisitorAddMovedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view movedCppType, + bool arrayElement = false, std::string_view parentState = {}, + std::string_view parentAccessor = {}) const noexcept; + void outputResponseFieldVisitorAddString( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddEnum(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement = false, + std::string_view parentState = {}, std::string_view parentAccessor = {}, + std::string_view parentCppType = {}) const noexcept; + void outputResponseFieldVisitorAddId( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddCopiedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view copiedCppType, + bool arrayElement = false, std::string_view parentState = {}, + std::string_view parentAccessor = {}) const noexcept; + void outputResponseFieldVisitorAddBool( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddInt( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; + void outputResponseFieldVisitorAddFloat( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept; const SchemaLoader _schemaLoader; const RequestLoader _requestLoader; diff --git a/include/graphqlservice/GraphQLResponse.h b/include/graphqlservice/GraphQLResponse.h index f155c04f..c4bd7765 100644 --- a/include/graphqlservice/GraphQLResponse.h +++ b/include/graphqlservice/GraphQLResponse.h @@ -6,13 +6,14 @@ #ifndef GRAPHQLRESPONSE_H #define GRAPHQLRESPONSE_H -#include "internal/DllExports.h" #include "internal/Awaitable.h" +#include "internal/DllExports.h" #include #include #include #include +#include #include #include #include @@ -375,6 +376,292 @@ GRAPHQLRESPONSE_EXPORT IdType Value::release(); using AwaitableValue = internal::Awaitable; +// Type-erased visitor for alternate representations of Value. +class [[nodiscard("unnecessary construction")]] ValueVisitor final + : public std::enable_shared_from_this +{ +private: + struct Concept + { + virtual ~Concept() = default; + + virtual void add_value(std::shared_ptr&& value) = 0; + + virtual void reserve(std::size_t count) = 0; + + virtual void start_object() = 0; + virtual void add_member(std::string&& key) = 0; + virtual void end_object() = 0; + + virtual void start_array() = 0; + virtual void end_array() = 0; + + virtual void add_null() = 0; + virtual void add_string(std::string&& value) = 0; + virtual void add_enum(std::string&& value) = 0; + virtual void add_id(IdType&& value) = 0; + virtual void add_bool(bool value) = 0; + virtual void add_int(int value) = 0; + virtual void add_float(double value) = 0; + + virtual void complete() = 0; + }; + + template + struct Model : Concept + { + explicit Model(std::shared_ptr pimpl) noexcept + : _pimpl { std::move(pimpl) } + { + } + + void add_value(std::shared_ptr&& value) final + { + _pimpl->add_value(std::move(value)); + } + + void reserve(std::size_t count) final + { + _pimpl->reserve(count); + } + + void start_object() final + { + _pimpl->start_object(); + } + + void add_member(std::string&& key) final + { + _pimpl->add_member(std::move(key)); + } + + void end_object() final + { + _pimpl->end_object(); + } + + void start_array() final + { + _pimpl->start_array(); + } + + void end_array() final + { + _pimpl->end_array(); + } + + void add_null() final + { + _pimpl->add_null(); + } + + void add_string(std::string&& value) final + { + _pimpl->add_string(std::move(value)); + } + + void add_enum(std::string&& value) final + { + _pimpl->add_enum(std::move(value)); + } + + void add_id(IdType&& value) final + { + _pimpl->add_id(std::move(value)); + } + + void add_bool(bool value) final + { + _pimpl->add_bool(value); + } + + void add_int(int value) final + { + _pimpl->add_int(value); + } + + void add_float(double value) final + { + _pimpl->add_float(value); + } + + void complete() final + { + _pimpl->complete(); + } + + private: + std::shared_ptr _pimpl; + }; + + const std::shared_ptr _concept; + +public: + template + ValueVisitor(std::shared_ptr writer) noexcept + : _concept { std::static_pointer_cast( + std::make_shared>(std::move(writer))) } + { + } + + GRAPHQLRESPONSE_EXPORT void add_value(std::shared_ptr&& value); + + GRAPHQLRESPONSE_EXPORT void reserve(std::size_t count); + + GRAPHQLRESPONSE_EXPORT void start_object(); + GRAPHQLRESPONSE_EXPORT void add_member(std::string&& key); + GRAPHQLRESPONSE_EXPORT void end_object(); + + GRAPHQLRESPONSE_EXPORT void start_array(); + GRAPHQLRESPONSE_EXPORT void end_array(); + + GRAPHQLRESPONSE_EXPORT void add_null(); + GRAPHQLRESPONSE_EXPORT void add_string(std::string&& value); + GRAPHQLRESPONSE_EXPORT void add_enum(std::string&& value); + GRAPHQLRESPONSE_EXPORT void add_id(IdType&& value); + GRAPHQLRESPONSE_EXPORT void add_bool(bool value); + GRAPHQLRESPONSE_EXPORT void add_int(int value); + GRAPHQLRESPONSE_EXPORT void add_float(double value); + + GRAPHQLRESPONSE_EXPORT void complete(); +}; + +// Pending token for ValueVisitor. +struct [[nodiscard("unnecessary construction")]] ValueToken +{ + using OpaqueValue = std::shared_ptr; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(OpaqueValue&& value); + + struct Reserve + { + std::size_t capacity; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(Reserve&& value); + + struct StartObject + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(StartObject&& value); + + struct AddMember + { + std::string key; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(AddMember&& value); + + struct EndObject + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(EndObject&& value); + + struct StartArray + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(StartArray&& value); + + struct EndArray + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(EndArray&& value); + + struct NullValue + { + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(NullValue&& value); + + struct StringValue + { + std::string value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(StringValue&& value); + + struct EnumValue + { + std::string value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(EnumValue&& value); + + struct IdValue + { + IdType value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(IdValue&& value); + + struct BoolValue + { + bool value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(BoolValue&& value); + + struct IntValue + { + int value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(IntValue&& value); + + struct FloatValue + { + double value; + }; + + GRAPHQLRESPONSE_EXPORT explicit ValueToken(FloatValue&& value); + + GRAPHQLRESPONSE_EXPORT void visit(const std::shared_ptr& visitor) &&; + +private: + using variant_type = + std::variant; + + variant_type _value; +}; + +class [[nodiscard("unnecessary construction")]] ValueTokenStream final +{ +public: + ValueTokenStream() noexcept = default; + ~ValueTokenStream() = default; + + ValueTokenStream(ValueTokenStream&&) noexcept = default; + ValueTokenStream& operator=(ValueTokenStream&&) noexcept = default; + + ValueTokenStream(const ValueTokenStream&) = delete; + ValueTokenStream& operator=(const ValueTokenStream&) = delete; + + template + ValueTokenStream(TArg&& arg) + : _tokens { ValueToken { std::forward(arg) } } + { + } + + template + void push_back(TArg&& arg) + { + _tokens.push_back(ValueToken { std::forward(arg) }); + } + + GRAPHQLRESPONSE_EXPORT void append(ValueTokenStream&& other); + + GRAPHQLRESPONSE_EXPORT void visit(const std::shared_ptr& visitor) &&; + GRAPHQLRESPONSE_EXPORT Value value() &&; + +private: + std::list _tokens; +}; + class [[nodiscard("unnecessary construction")]] Writer final { private: diff --git a/include/graphqlservice/GraphQLService.h b/include/graphqlservice/GraphQLService.h index 99c8aff1..95cba892 100644 --- a/include/graphqlservice/GraphQLService.h +++ b/include/graphqlservice/GraphQLService.h @@ -539,7 +539,9 @@ struct [[nodiscard("unnecessary construction")]] ResolverParams : SelectionSetPa // we're ready to return from the top level Operation. struct [[nodiscard("unnecessary construction")]] ResolverResult { - response::Value data; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT response::Value document() &&; + + response::ValueTokenStream data {}; std::list errors {}; }; @@ -1019,7 +1021,7 @@ struct ModifiedResult if (!awaitedResult) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto modifiedResult = @@ -1046,8 +1048,8 @@ struct ModifiedResult if (value) { ModifiedResult::validateScalar(*value); - co_return ResolverResult { response::Value { - std::shared_ptr { std::move(value) } } }; + co_return ResolverResult { { response::ValueToken::OpaqueValue { + std::shared_ptr { std::move(value) } } } }; } } @@ -1060,7 +1062,7 @@ struct ModifiedResult if (!awaitedResult) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto modifiedResult = co_await ModifiedResult::convert(std::move(*awaitedResult), @@ -1083,8 +1085,8 @@ struct ModifiedResult if (value) { ModifiedResult::validateScalar(*value); - co_return ResolverResult { response::Value { - std::shared_ptr { std::move(value) } } }; + co_return ResolverResult { { response::ValueToken::OpaqueValue { + std::shared_ptr { std::move(value) } } } }; } } @@ -1128,9 +1130,10 @@ struct ModifiedResult } } - ResolverResult document { response::Value { response::Type::List } }; + ResolverResult document; - document.data.reserve(children.size()); + document.data.push_back(response::ValueToken::StartArray {}); + document.data.push_back(response::ValueToken::Reserve { children.size() }); std::get(params.errorPath->segment) = 0; for (auto& child : children) @@ -1141,11 +1144,11 @@ struct ModifiedResult auto value = co_await std::move(child); - document.data.emplace_back(std::move(value.data)); + document.data.append(std::move(value.data)); if (!value.errors.empty()) { - document.errors.splice(document.errors.end(), value.errors); + document.errors.splice(document.errors.end(), std::move(value.errors)); } } catch (schema_exception& scx) @@ -1171,6 +1174,8 @@ struct ModifiedResult ++std::get(params.errorPath->segment); } + document.data.push_back(response::ValueToken::EndArray {}); + co_return document; } @@ -1213,7 +1218,7 @@ struct ModifiedResult } using ResolverCallback = - std::function::type, const ResolverParams&)>; + std::function::type, const ResolverParams&)>; [[nodiscard("unnecessary call")]] static AwaitableResolver resolve( typename ResultTraits::future_type result, ResolverParams&& paramsArg, @@ -1226,7 +1231,8 @@ struct ModifiedResult if (value) { Result::validateScalar(*value); - co_return ResolverResult { response::Value { std::shared_ptr { std::move(value) } } }; + co_return ResolverResult { { response::ValueToken::OpaqueValue { + std::shared_ptr { std::move(value) } } } }; } auto pendingResolver = std::move(resolver); @@ -1238,7 +1244,7 @@ struct ModifiedResult try { co_await params.launch; - document.data = pendingResolver(co_await result, params); + document = pendingResolver(co_await result, params); } catch (schema_exception& scx) { @@ -1279,6 +1285,8 @@ using ObjectResult = ModifiedResult; // Subscription callbacks receive the response::Value representing the result of evaluating the // SelectionSet against the payload. using SubscriptionCallback = std::function; +using SubscriptionVisitor = std::function; +using SubscriptionCallbackOrVisitor = std::variant; // Subscriptions are stored in maps using these keys. using SubscriptionKey = std::size_t; @@ -1305,7 +1313,7 @@ struct [[nodiscard("unnecessary construction")]] RequestResolveParams struct [[nodiscard("unnecessary construction")]] RequestSubscribeParams { // Callback which receives the event data. - SubscriptionCallback callback; + SubscriptionCallbackOrVisitor callback; // Required query information. peg::ast query; @@ -1396,7 +1404,7 @@ struct [[nodiscard("unnecessary construction")]] SubscriptionData { explicit SubscriptionData(std::shared_ptr data, SubscriptionName&& field, response::Value arguments, Directives fieldDirectives, peg::ast&& query, - std::string&& operationName, SubscriptionCallback&& callback, + std::string&& operationName, SubscriptionCallbackOrVisitor&& callback, const peg::ast_node& selection); std::shared_ptr data; @@ -1406,7 +1414,7 @@ struct [[nodiscard("unnecessary construction")]] SubscriptionData Directives fieldDirectives; peg::ast query; std::string operationName; - SubscriptionCallback callback; + SubscriptionCallbackOrVisitor callback; const peg::ast_node& selection; }; @@ -1441,6 +1449,8 @@ class [[nodiscard("unnecessary construction")]] Request [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT response::AwaitableValue resolve( RequestResolveParams params) const; + [[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT AwaitableResolver visit( + RequestResolveParams params) const; [[nodiscard("leaked subscription")]] GRAPHQLSERVICE_EXPORT AwaitableSubscribe subscribe( RequestSubscribeParams params); [[nodiscard("potentially leaked subscription")]] GRAPHQLSERVICE_EXPORT AwaitableUnsubscribe diff --git a/include/graphqlservice/Response.ixx b/include/graphqlservice/Response.ixx index 5e4160ce..4f83da5d 100644 --- a/include/graphqlservice/Response.ixx +++ b/include/graphqlservice/Response.ixx @@ -28,6 +28,10 @@ using response::ValueTypeTraits; using response::Value; using response::AwaitableValue; +using response::ValueVisitor; +using response::ValueToken; +using response::ValueTokenStream; + using response::Writer; // clang-format on diff --git a/include/graphqlservice/Service.ixx b/include/graphqlservice/Service.ixx index a7cd01f2..63cc8119 100644 --- a/include/graphqlservice/Service.ixx +++ b/include/graphqlservice/Service.ixx @@ -100,6 +100,8 @@ using modified_result::ScalarResult; using modified_result::ObjectResult; using service::SubscriptionCallback; +using service::SubscriptionVisitor; +using service::SubscriptionCallbackOrVisitor; using service::SubscriptionKey; using service::SubscriptionName; diff --git a/samples/client/benchmark.cpp b/samples/client/benchmark.cpp index 66723db2..41951aa3 100644 --- a/samples/client/benchmark.cpp +++ b/samples/client/benchmark.cpp @@ -86,7 +86,6 @@ int main(int argc, char** argv) const auto mockService = today::mock_service(); const auto& service = mockService->service; std::vector durationResolve(iterations); - std::vector durationParseServiceResponse(iterations); std::vector durationParseResponse(iterations); const auto startTime = std::chrono::steady_clock::now(); @@ -96,19 +95,21 @@ int main(int argc, char** argv) auto query = GetRequestObject(); const auto& name = GetOperationName(); + auto visitor = std::make_shared(); + auto responseVisitor = std::make_shared(visitor); for (std::size_t i = 0; i < iterations; ++i) { const auto startResolve = std::chrono::steady_clock::now(); - auto response = service->resolve({ query, name }).get(); - const auto startParseServiceResponse = std::chrono::steady_clock::now(); - auto serviceResponse = client::parseServiceResponse(std::move(response)); + auto response = service->visit({ query, name }).get(); const auto startParseResponse = std::chrono::steady_clock::now(); - const auto parsed = parseResponse(std::move(serviceResponse.data)); + + std::move(response.data).visit(responseVisitor); + + const auto parsed = visitor->response(); const auto endParseResponse = std::chrono::steady_clock::now(); - durationResolve[i] = startParseServiceResponse - startResolve; - durationParseServiceResponse[i] = startParseResponse - startParseServiceResponse; + durationResolve[i] = startParseResponse - startResolve; durationParseResponse[i] = endParseResponse - startParseResponse; } } @@ -124,7 +125,6 @@ int main(int argc, char** argv) outputOverview(iterations, totalDuration); outputSegment("Resolve"sv, durationResolve); - outputSegment("ParseServiceResponse"sv, durationParseServiceResponse); outputSegment("ParseResponse"sv, durationParseResponse); return 0; diff --git a/samples/client/benchmark/TodayClient.cpp b/samples/client/benchmark/TodayClient.cpp index 0f65eae7..007b7aad 100644 --- a/samples/client/benchmark/TodayClient.cpp +++ b/samples/client/benchmark/TodayClient.cpp @@ -185,6 +185,405 @@ const std::string& GetOperationName() noexcept return s_name; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_appointments, + Member_appointments_pageInfo, + Member_appointments_pageInfo_hasNextPage, + Member_appointments_edges, + Member_appointments_edges_0, + Member_appointments_edges_0_, + Member_appointments_edges_0_node, + Member_appointments_edges_0_node_id, + Member_appointments_edges_0_node_when, + Member_appointments_edges_0_node_subject, + Member_appointments_edges_0_node_isNow, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.appointments = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_pageInfo: + _pimpl->state = impl::VisitorState::Member_appointments; + _pimpl->response.appointments.pageInfo = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_pageInfo_hasNextPage: + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo; + _pimpl->response.appointments.pageInfo.hasNextPage = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->response.appointments.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "appointments"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments; + } + break; + + case impl::VisitorState::Member_appointments: + if (key == "pageInfo"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo; + } + else if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges; + } + break; + + case impl::VisitorState::Member_appointments_pageInfo: + if (key == "hasNextPage"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo_hasNextPage; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_id; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_when; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_subject; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_isNow; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_pageInfo: + _pimpl->state = impl::VisitorState::Member_appointments; + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + break; + + case impl::VisitorState::Member_appointments_edges_0_: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + break; + + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + _pimpl->response.appointments.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_pageInfo_hasNextPage: + _pimpl->state = impl::VisitorState::Member_appointments_pageInfo; + _pimpl->response.appointments.pageInfo.hasNextPage = value; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; diff --git a/samples/client/benchmark/TodayClient.h b/samples/client/benchmark/TodayClient.h index 453685fe..6acd89b6 100644 --- a/samples/client/benchmark/TodayClient.h +++ b/samples/client/benchmark/TodayClient.h @@ -92,6 +92,37 @@ struct [[nodiscard("unnecessary construction")]] Response appointments_AppointmentConnection appointments {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -101,6 +132,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Query::Response; + using ResponseVisitor = Query::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; diff --git a/samples/client/benchmark/TodayClient.ixx b/samples/client/benchmark/TodayClient.ixx index 5631ad46..6ddf5cdc 100644 --- a/samples/client/benchmark/TodayClient.ixx +++ b/samples/client/benchmark/TodayClient.ixx @@ -23,6 +23,7 @@ using graphql::today::client::GetRequestObject; using Query::GetOperationName; using Query::Response; +using Query::ResponseVisitor; using Query::parseResponse; using Query::Traits; diff --git a/samples/client/multiple/MultipleQueriesClient.cpp b/samples/client/multiple/MultipleQueriesClient.cpp index 92959f11..1644ddcb 100644 --- a/samples/client/multiple/MultipleQueriesClient.cpp +++ b/samples/client/multiple/MultipleQueriesClient.cpp @@ -285,6 +285,388 @@ const std::string& GetOperationName() noexcept return s_name; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_appointments, + Member_appointments_edges, + Member_appointments_edges_0, + Member_appointments_edges_0_, + Member_appointments_edges_0_node, + Member_appointments_edges_0_node_id, + Member_appointments_edges_0_node_subject, + Member_appointments_edges_0_node_when, + Member_appointments_edges_0_node_isNow, + Member_appointments_edges_0_node__typename, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.appointments = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->response.appointments.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "appointments"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments; + } + break; + + case impl::VisitorState::Member_appointments: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_id; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_subject; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_isNow; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node__typename; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + break; + + case impl::VisitorState::Member_appointments_edges_0_: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + break; + + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + _pimpl->response.appointments.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::move(value); + break; + + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; @@ -425,138 +807,505 @@ const std::string& GetOperationName() noexcept return s_name; } -Response parseResponse(response::Value&& response) +struct ResponseVisitor::impl { - using namespace graphql::client; - - Response result; - - if (response.type() == response::Type::Map) + enum class VisitorState { - auto members = response.release(); + Start, + Member_tasks, + Member_tasks_edges, + Member_tasks_edges_0, + Member_tasks_edges_0_, + Member_tasks_edges_0_node, + Member_tasks_edges_0_node_id, + Member_tasks_edges_0_node_title, + Member_tasks_edges_0_node_isComplete, + Member_tasks_edges_0_node__typename, + Complete, + }; - for (auto& member : members) - { - if (member.first == R"js(tasks)js"sv) - { - result.tasks = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } + VisitorState state { VisitorState::Start }; + Response response {}; +}; - return result; +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +ResponseVisitor::~ResponseVisitor() { - return client::GetRequestText(); } -[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) { - return client::GetRequestObject(); + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.tasks = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) { - return Tasks::GetOperationName(); + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +void ResponseVisitor::start_object() { - return Tasks::parseResponse(std::move(response)); -} + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->push_back(std::make_optional({})); + break; -} // namespace multiple::client::query::Tasks -namespace client { + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->response.tasks.edges->back()->node = std::make_optional({}); + break; -using namespace multiple; + case impl::VisitorState::Complete: + break; -template <> -graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder Response::parse(response::Value&& response) -{ - graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder result; + default: + break; + } +} - if (response.type() == response::Type::Map) +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Start: + if (key == "tasks"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks; + } + break; - for (auto& member : members) - { - if (member.first == R"js(id)js"sv) + case impl::VisitorState::Member_tasks: + if (key == "edges"sv) { - result.id = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges; } - if (member.first == R"js(name)js"sv) + break; + + case impl::VisitorState::Member_tasks_edges_0_: + if (key == "node"sv) { - result.name = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; } - if (member.first == R"js(unreadCount)js"sv) + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + if (key == "id"sv) { - result.unreadCount = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_id; } - if (member.first == R"js(__typename)js"sv) + else if (key == "title"sv) { - result._typename = ModifiedResponse::parse(std::move(member.second)); - continue; + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_title; } - } - } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_isComplete; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node__typename; + } + break; - return result; + case impl::VisitorState::Complete: + break; + + default: + break; + } } -template <> -graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge Response::parse(response::Value&& response) +void ResponseVisitor::end_object() { - graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge result; - - if (response.type() == response::Type::Map) + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + break; - for (auto& member : members) - { - if (member.first == R"js(node)js"sv) - { - result.node = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } + case impl::VisitorState::Member_tasks_edges_0_: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + break; - return result; + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -template <> -graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection Response::parse(response::Value&& response) +void ResponseVisitor::start_array() { - graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection result; - - if (response.type() == response::Type::Map) + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Member_tasks_edges: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + _pimpl->response.tasks.edges = std::make_optional>>({}); + break; - for (auto& member : members) - { - if (member.first == R"js(edges)js"sv) - { - result.edges = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } + case impl::VisitorState::Complete: + break; - return result; + default: + break; + } } -} // namespace client +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks; + break; -namespace multiple::client::query::UnreadCounts { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + +Response parseResponse(response::Value&& response) +{ + using namespace graphql::client; + + Response result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(tasks)js"sv) + { + result.tasks = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +{ + return client::GetRequestText(); +} + +[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +{ + return client::GetRequestObject(); +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +{ + return Tasks::GetOperationName(); +} + +[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +{ + return Tasks::parseResponse(std::move(response)); +} + +} // namespace multiple::client::query::Tasks +namespace client { + +using namespace multiple; + +template <> +graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge::node_Folder result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(id)js"sv) + { + result.id = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(name)js"sv) + { + result.name = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(unreadCount)js"sv) + { + result.unreadCount = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(__typename)js"sv) + { + result._typename = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +template <> +graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection::edges_FolderEdge result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(node)js"sv) + { + result.node = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +template <> +graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::UnreadCounts::Response::unreadCounts_FolderConnection result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(edges)js"sv) + { + result.edges = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +} // namespace client + +namespace multiple::client::query::UnreadCounts { const std::string& GetOperationName() noexcept { @@ -565,143 +1314,921 @@ const std::string& GetOperationName() noexcept return s_name; } -Response parseResponse(response::Value&& response) +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_unreadCounts, + Member_unreadCounts_edges, + Member_unreadCounts_edges_0, + Member_unreadCounts_edges_0_, + Member_unreadCounts_edges_0_node, + Member_unreadCounts_edges_0_node_id, + Member_unreadCounts_edges_0_node_name, + Member_unreadCounts_edges_0_node_unreadCount, + Member_unreadCounts_edges_0_node__typename, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.unreadCounts = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->response.unreadCounts.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "unreadCounts"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts; + } + break; + + case impl::VisitorState::Member_unreadCounts: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_id; + } + else if (key == "name"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_name; + } + else if (key == "unreadCount"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node__typename; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + break; + + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + _pimpl->response.unreadCounts.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + +Response parseResponse(response::Value&& response) +{ + using namespace graphql::client; + + Response result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(unreadCounts)js"sv) + { + result.unreadCounts = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +{ + return client::GetRequestText(); +} + +[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +{ + return client::GetRequestObject(); +} + +[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +{ + return UnreadCounts::GetOperationName(); +} + +[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +{ + return UnreadCounts::parseResponse(std::move(response)); +} + +} // namespace multiple::client::query::UnreadCounts + +namespace client { + +using namespace multiple; + +static const std::array, 4> s_valuesTaskState = { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) +}; + +template <> +TaskState Response::parse(response::Value&& value) +{ + if (!value.maybe_enum()) + { + throw std::logic_error { R"ex(not a valid TaskState value)ex" }; + } + + const auto result = internal::sorted_map_lookup( + s_valuesTaskState, + std::string_view { value.get() }); + + if (!result) + { + throw std::logic_error { R"ex(not a valid TaskState value)ex" }; + } + + return *result; +} + +template <> +graphql::multiple::client::query::Miscellaneous::Response::anyType_UnionType Response::parse(response::Value&& response) +{ + graphql::multiple::client::query::Miscellaneous::Response::anyType_UnionType result; + + if (response.type() == response::Type::Map) + { + auto members = response.release(); + + for (auto& member : members) + { + if (member.first == R"js(__typename)js"sv) + { + result._typename = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(id)js"sv) + { + result.id = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(title)js"sv) + { + result.title = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(isComplete)js"sv) + { + result.isComplete = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(subject)js"sv) + { + result.subject = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(when)js"sv) + { + result.when = ModifiedResponse::parse(std::move(member.second)); + continue; + } + if (member.first == R"js(isNow)js"sv) + { + result.isNow = ModifiedResponse::parse(std::move(member.second)); + continue; + } + } + } + + return result; +} + +} // namespace client + +namespace multiple::client::query::Miscellaneous { + +const std::string& GetOperationName() noexcept +{ + static const auto s_name = R"gql(Miscellaneous)gql"s; + + return s_name; +} + +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_testTaskState, + Member_anyType, + Member_anyType_0, + Member_anyType_0_, + Member_anyType_0__typename, + Member_anyType_0_id, + Member_anyType_0_title, + Member_anyType_0_isComplete, + Member_anyType_0_subject, + Member_anyType_0_when, + Member_anyType_0_isNow, + Member_default_, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.testTaskState = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.push_back(std::make_optional({})); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "testTaskState"sv) + { + _pimpl->state = impl::VisitorState::Member_testTaskState; + } + else if (key == "anyType"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType; + } + else if (key == "default"sv) + { + _pimpl->state = impl::VisitorState::Member_default_; + } + break; + + case impl::VisitorState::Member_anyType_0_: + if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0__typename; + } + else if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_id; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isComplete; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_subject; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isNow; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() { - using namespace graphql::client; + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0_: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; - Response result; + case impl::VisitorState::Complete: + break; - if (response.type() == response::Type::Map) + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Member_anyType: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; - for (auto& member : members) - { - if (member.first == R"js(unreadCounts)js"sv) - { - result.unreadCounts = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } - } + case impl::VisitorState::Complete: + break; - return result; + default: + break; + } } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetRequestText() noexcept +void ResponseVisitor::end_array() { - return client::GetRequestText(); + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary call")]] const peg::ast& Traits::GetRequestObject() noexcept +void ResponseVisitor::add_null() { - return client::GetRequestObject(); + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(std::nullopt); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = std::nullopt; + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary call")]] const std::string& Traits::GetOperationName() noexcept +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) { - return UnreadCounts::GetOperationName(); + switch (_pimpl->state) + { + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::move(value); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -[[nodiscard("unnecessary conversion")]] Traits::Response Traits::parseResponse(response::Value&& response) +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) { - return UnreadCounts::parseResponse(std::move(response)); -} + using namespace graphql::client; -} // namespace multiple::client::query::UnreadCounts + switch (_pimpl->state) + { + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + if (const auto enumValue = internal::sorted_map_lookup(s_valuesTaskState, std::string_view { value })) + { + _pimpl->response.testTaskState = *enumValue; + } + break; -namespace client { + case impl::VisitorState::Complete: + break; -using namespace multiple; + default: + break; + } +} -template <> -TaskState Response::parse(response::Value&& value) +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) { - if (!value.maybe_enum()) + switch (_pimpl->state) { - throw std::logic_error { R"ex(not a valid TaskState value)ex" }; - } + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = std::move(value); + break; - static const std::array, 4> s_values = { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; + case impl::VisitorState::Complete: + break; - const auto result = internal::sorted_map_lookup( - s_values, - std::string_view { value.get() }); + default: + break; + } +} - if (!result) +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) { - throw std::logic_error { R"ex(not a valid TaskState value)ex" }; - } + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = value; + break; - return *result; + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } } -template <> -graphql::multiple::client::query::Miscellaneous::Response::anyType_UnionType Response::parse(response::Value&& response) +void ResponseVisitor::add_int([[maybe_unused]] int value) { - graphql::multiple::client::query::Miscellaneous::Response::anyType_UnionType result; + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; - if (response.type() == response::Type::Map) + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) { - auto members = response.release(); + case impl::VisitorState::Complete: + break; - for (auto& member : members) - { - if (member.first == R"js(__typename)js"sv) - { - result._typename = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(id)js"sv) - { - result.id = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(title)js"sv) - { - result.title = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(isComplete)js"sv) - { - result.isComplete = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(subject)js"sv) - { - result.subject = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(when)js"sv) - { - result.when = ModifiedResponse::parse(std::move(member.second)); - continue; - } - if (member.first == R"js(isNow)js"sv) - { - result.isNow = ModifiedResponse::parse(std::move(member.second)); - continue; - } - } + default: + break; } +} - return result; +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; } -} // namespace client +Response ResponseVisitor::response() +{ + Response response {}; -namespace multiple::client::query::Miscellaneous { + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; -const std::string& GetOperationName() noexcept -{ - static const auto s_name = R"gql(Miscellaneous)gql"s; + default: + break; + } - return s_name; + return response; } Response parseResponse(response::Value&& response) @@ -875,6 +2402,338 @@ response::Value serializeVariables(Variables&& variables) return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_completedTask, + Member_completedTask_completedTask, + Member_completedTask_completedTask_completedTaskId, + Member_completedTask_completedTask_title, + Member_completedTask_completedTask_isComplete, + Member_completedTask_clientMutationId, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->response.completedTask.completedTask = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask; + } + break; + + case impl::VisitorState::Member_completedTask: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + } + else if (key == "clientMutationId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_clientMutationId; + } + break; + + case impl::VisitorState::Member_completedTask_completedTask: + if (key == "completedTaskId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_completedTaskId; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_isComplete; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + break; + + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::move(value); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; diff --git a/samples/client/multiple/MultipleQueriesClient.h b/samples/client/multiple/MultipleQueriesClient.h index 8806bec7..4e711034 100644 --- a/samples/client/multiple/MultipleQueriesClient.h +++ b/samples/client/multiple/MultipleQueriesClient.h @@ -180,6 +180,37 @@ struct [[nodiscard("unnecessary construction")]] Response appointments_AppointmentConnection appointments {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -189,6 +220,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Appointments::Response; + using ResponseVisitor = Appointments::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -226,6 +258,37 @@ struct [[nodiscard("unnecessary construction")]] Response tasks_TaskConnection tasks {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -235,6 +298,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Tasks::Response; + using ResponseVisitor = Tasks::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -272,6 +336,37 @@ struct [[nodiscard("unnecessary construction")]] Response unreadCounts_FolderConnection unreadCounts {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -281,6 +376,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = UnreadCounts::Response; + using ResponseVisitor = UnreadCounts::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -315,6 +411,37 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional default_ {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -324,6 +451,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Miscellaneous::Response; + using ResponseVisitor = Miscellaneous::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -368,6 +496,37 @@ struct [[nodiscard("unnecessary construction")]] Response completedTask_CompleteTaskPayload completedTask {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -381,6 +540,7 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = CompleteTaskMutation::Response; + using ResponseVisitor = CompleteTaskMutation::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; diff --git a/samples/client/multiple/MultipleQueriesClient.ixx b/samples/client/multiple/MultipleQueriesClient.ixx index d21f7130..22f60abd 100644 --- a/samples/client/multiple/MultipleQueriesClient.ixx +++ b/samples/client/multiple/MultipleQueriesClient.ixx @@ -31,6 +31,7 @@ using graphql::multiple::client::GetRequestObject; using Appointments::GetOperationName; using Appointments::Response; +using Appointments::ResponseVisitor; using Appointments::parseResponse; using Appointments::Traits; @@ -44,6 +45,7 @@ using graphql::multiple::client::GetRequestObject; using Tasks::GetOperationName; using Tasks::Response; +using Tasks::ResponseVisitor; using Tasks::parseResponse; using Tasks::Traits; @@ -57,6 +59,7 @@ using graphql::multiple::client::GetRequestObject; using UnreadCounts::GetOperationName; using UnreadCounts::Response; +using UnreadCounts::ResponseVisitor; using UnreadCounts::parseResponse; using UnreadCounts::Traits; @@ -72,6 +75,7 @@ using Miscellaneous::GetOperationName; using graphql::multiple::TaskState; using Miscellaneous::Response; +using Miscellaneous::ResponseVisitor; using Miscellaneous::parseResponse; using Miscellaneous::Traits; @@ -92,6 +96,7 @@ using CompleteTaskMutation::Variables; using CompleteTaskMutation::serializeVariables; using CompleteTaskMutation::Response; +using CompleteTaskMutation::ResponseVisitor; using CompleteTaskMutation::parseResponse; using CompleteTaskMutation::Traits; diff --git a/samples/client/mutate/MutateClient.cpp b/samples/client/mutate/MutateClient.cpp index a0f26f85..f54db298 100644 --- a/samples/client/mutate/MutateClient.cpp +++ b/samples/client/mutate/MutateClient.cpp @@ -152,6 +152,13 @@ response::Value Variable::serialize(CompleteTaskInput&& input return result; } +static const std::array, 4> s_valuesTaskState = { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) +}; + template <> TaskState Response::parse(response::Value&& value) { @@ -160,15 +167,8 @@ TaskState Response::parse(response::Value&& value) throw std::logic_error { R"ex(not a valid TaskState value)ex" }; } - static const std::array, 4> s_values = { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; - const auto result = internal::sorted_map_lookup( - s_values, + s_valuesTaskState, std::string_view { value.get() }); if (!result) @@ -261,6 +261,338 @@ response::Value serializeVariables(Variables&& variables) return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_completedTask, + Member_completedTask_completedTask, + Member_completedTask_completedTask_completedTaskId, + Member_completedTask_completedTask_title, + Member_completedTask_completedTask_isComplete, + Member_completedTask_clientMutationId, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->response.completedTask.completedTask = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask; + } + break; + + case impl::VisitorState::Member_completedTask: + if (key == "completedTask"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + } + else if (key == "clientMutationId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_clientMutationId; + } + break; + + case impl::VisitorState::Member_completedTask_completedTask: + if (key == "completedTaskId"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_completedTaskId; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask_isComplete; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + break; + + case impl::VisitorState::Member_completedTask: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.completedTask = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::nullopt; + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_title: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->title = std::move(value); + break; + + case impl::VisitorState::Member_completedTask_clientMutationId: + _pimpl->state = impl::VisitorState::Member_completedTask; + _pimpl->response.completedTask.clientMutationId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_completedTaskId: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->completedTaskId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_completedTask_completedTask_isComplete: + _pimpl->state = impl::VisitorState::Member_completedTask_completedTask; + _pimpl->response.completedTask.completedTask->isComplete = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; diff --git a/samples/client/mutate/MutateClient.h b/samples/client/mutate/MutateClient.h index ab016aaa..4c945b24 100644 --- a/samples/client/mutate/MutateClient.h +++ b/samples/client/mutate/MutateClient.h @@ -119,6 +119,37 @@ struct [[nodiscard("unnecessary construction")]] Response completedTask_CompleteTaskPayload completedTask {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -132,6 +163,7 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = CompleteTaskMutation::Response; + using ResponseVisitor = CompleteTaskMutation::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; diff --git a/samples/client/mutate/MutateClient.ixx b/samples/client/mutate/MutateClient.ixx index 4e781529..6015a4cb 100644 --- a/samples/client/mutate/MutateClient.ixx +++ b/samples/client/mutate/MutateClient.ixx @@ -38,6 +38,7 @@ using CompleteTaskMutation::Variables; using CompleteTaskMutation::serializeVariables; using CompleteTaskMutation::Response; +using CompleteTaskMutation::ResponseVisitor; using CompleteTaskMutation::parseResponse; using CompleteTaskMutation::Traits; diff --git a/samples/client/nestedinput/NestedInputClient.cpp b/samples/client/nestedinput/NestedInputClient.cpp index a70c79ba..549132ac 100644 --- a/samples/client/nestedinput/NestedInputClient.cpp +++ b/samples/client/nestedinput/NestedInputClient.cpp @@ -356,6 +356,288 @@ response::Value serializeVariables(Variables&& variables) return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_control, + Member_control_test, + Member_control_test_id, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_control: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.control = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_control_test: + _pimpl->state = impl::VisitorState::Member_control; + _pimpl->response.control.test = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_control_test_id: + _pimpl->state = impl::VisitorState::Member_control_test; + _pimpl->response.control.test->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test: + _pimpl->response.control.test = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "control"sv) + { + _pimpl->state = impl::VisitorState::Member_control; + } + break; + + case impl::VisitorState::Member_control: + if (key == "test"sv) + { + _pimpl->state = impl::VisitorState::Member_control_test; + } + break; + + case impl::VisitorState::Member_control_test: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_control_test_id; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test: + _pimpl->state = impl::VisitorState::Member_control; + break; + + case impl::VisitorState::Member_control: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test: + _pimpl->state = impl::VisitorState::Member_control; + _pimpl->response.control.test = std::nullopt; + break; + + case impl::VisitorState::Member_control_test_id: + _pimpl->state = impl::VisitorState::Member_control_test; + _pimpl->response.control.test->id = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_control_test_id: + _pimpl->state = impl::VisitorState::Member_control_test; + _pimpl->response.control.test->id = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; diff --git a/samples/client/nestedinput/NestedInputClient.h b/samples/client/nestedinput/NestedInputClient.h index 2350eb51..06fc137d 100644 --- a/samples/client/nestedinput/NestedInputClient.h +++ b/samples/client/nestedinput/NestedInputClient.h @@ -153,6 +153,37 @@ struct [[nodiscard("unnecessary construction")]] Response control_Control control {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -166,6 +197,7 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = testQuery::Response; + using ResponseVisitor = testQuery::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; diff --git a/samples/client/nestedinput/NestedInputClient.ixx b/samples/client/nestedinput/NestedInputClient.ixx index 71430abe..44459e49 100644 --- a/samples/client/nestedinput/NestedInputClient.ixx +++ b/samples/client/nestedinput/NestedInputClient.ixx @@ -40,6 +40,7 @@ using testQuery::Variables; using testQuery::serializeVariables; using testQuery::Response; +using testQuery::ResponseVisitor; using testQuery::parseResponse; using testQuery::Traits; diff --git a/samples/client/query/QueryClient.cpp b/samples/client/query/QueryClient.cpp index 46f381f0..e6d9d180 100644 --- a/samples/client/query/QueryClient.cpp +++ b/samples/client/query/QueryClient.cpp @@ -110,6 +110,13 @@ namespace client { using namespace query; +static const std::array, 4> s_valuesTaskState = { + std::make_pair(R"gql(New)gql"sv, TaskState::New), + std::make_pair(R"gql(Started)gql"sv, TaskState::Started), + std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), + std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) +}; + template <> TaskState Response::parse(response::Value&& value) { @@ -118,15 +125,8 @@ TaskState Response::parse(response::Value&& value) throw std::logic_error { R"ex(not a valid TaskState value)ex" }; } - static const std::array, 4> s_values = { - std::make_pair(R"gql(New)gql"sv, TaskState::New), - std::make_pair(R"gql(Started)gql"sv, TaskState::Started), - std::make_pair(R"gql(Complete)gql"sv, TaskState::Complete), - std::make_pair(R"gql(Unassigned)gql"sv, TaskState::Unassigned) - }; - const auto result = internal::sorted_map_lookup( - s_values, + s_valuesTaskState, std::string_view { value.get() }); if (!result) @@ -448,6 +448,876 @@ const std::string& GetOperationName() noexcept return s_name; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_appointments, + Member_appointments_edges, + Member_appointments_edges_0, + Member_appointments_edges_0_, + Member_appointments_edges_0_node, + Member_appointments_edges_0_node_id, + Member_appointments_edges_0_node_subject, + Member_appointments_edges_0_node_when, + Member_appointments_edges_0_node_isNow, + Member_appointments_edges_0_node__typename, + Member_tasks, + Member_tasks_edges, + Member_tasks_edges_0, + Member_tasks_edges_0_, + Member_tasks_edges_0_node, + Member_tasks_edges_0_node_id, + Member_tasks_edges_0_node_title, + Member_tasks_edges_0_node_isComplete, + Member_tasks_edges_0_node__typename, + Member_unreadCounts, + Member_unreadCounts_edges, + Member_unreadCounts_edges_0, + Member_unreadCounts_edges_0_, + Member_unreadCounts_edges_0_node, + Member_unreadCounts_edges_0_node_id, + Member_unreadCounts_edges_0_node_name, + Member_unreadCounts_edges_0_node_unreadCount, + Member_unreadCounts_edges_0_node__typename, + Member_testTaskState, + Member_anyType, + Member_anyType_0, + Member_anyType_0_, + Member_anyType_0__typename, + Member_anyType_0_id, + Member_anyType_0_title, + Member_anyType_0_isComplete, + Member_anyType_0_subject, + Member_anyType_0_when, + Member_anyType_0_isNow, + Member_default_, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.appointments = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.tasks = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.unreadCounts = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.testTaskState = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->reserve(count); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->reserve(count); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->reserve(count); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->response.appointments.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->response.tasks.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->response.unreadCounts.edges->back()->node = std::make_optional({}); + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.push_back(std::make_optional({})); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "appointments"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments; + } + else if (key == "tasks"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks; + } + else if (key == "unreadCounts"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts; + } + else if (key == "testTaskState"sv) + { + _pimpl->state = impl::VisitorState::Member_testTaskState; + } + else if (key == "anyType"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType; + } + else if (key == "default"sv) + { + _pimpl->state = impl::VisitorState::Member_default_; + } + break; + + case impl::VisitorState::Member_appointments: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + } + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_id; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_subject; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node_isNow; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node__typename; + } + break; + + case impl::VisitorState::Member_tasks: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges; + } + break; + + case impl::VisitorState::Member_tasks_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + } + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_id; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node_isComplete; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node__typename; + } + break; + + case impl::VisitorState::Member_unreadCounts: + if (key == "edges"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + if (key == "node"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + } + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_id; + } + else if (key == "name"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_name; + } + else if (key == "unreadCount"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount; + } + else if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node__typename; + } + break; + + case impl::VisitorState::Member_anyType_0_: + if (key == "__typename"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0__typename; + } + else if (key == "id"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_id; + } + else if (key == "title"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_title; + } + else if (key == "isComplete"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isComplete; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_subject; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_when; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_isNow; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + break; + + case impl::VisitorState::Member_appointments_edges_0_: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + break; + + case impl::VisitorState::Member_appointments: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + break; + + case impl::VisitorState::Member_tasks_edges_0_: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + break; + + case impl::VisitorState::Member_tasks: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + break; + + case impl::VisitorState::Member_unreadCounts: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Member_anyType_0_: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0; + _pimpl->response.appointments.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Member_tasks_edges: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0; + _pimpl->response.tasks.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Member_unreadCounts_edges: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0; + _pimpl->response.unreadCounts.edges = std::make_optional>>({}); + break; + + case impl::VisitorState::Member_anyType: + _pimpl->state = impl::VisitorState::Member_anyType_0; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->state = impl::VisitorState::Member_appointments; + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->state = impl::VisitorState::Member_tasks; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->state = impl::VisitorState::Member_unreadCounts; + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0: + _pimpl->response.appointments.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_appointments_edges_0_node: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_; + _pimpl->response.appointments.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::nullopt; + break; + + case impl::VisitorState::Member_appointments_edges_0_node_when: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->when = std::nullopt; + break; + + case impl::VisitorState::Member_tasks_edges_0: + _pimpl->response.tasks.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_tasks_edges_0_node: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_; + _pimpl->response.tasks.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::nullopt; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0: + _pimpl->response.unreadCounts.edges->push_back(std::nullopt); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_; + _pimpl->response.unreadCounts.edges->back()->node = std::nullopt; + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0: + _pimpl->response.anyType.push_back(std::nullopt); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::nullopt; + break; + + case impl::VisitorState::Member_anyType_0_when: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->when = std::nullopt; + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_subject: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->subject = std::move(value); + break; + + case impl::VisitorState::Member_appointments_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_title: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->title = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_name: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->name = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node__typename: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->_typename = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0__typename: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->_typename = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_title: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->title = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_subject: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->subject = std::move(value); + break; + + case impl::VisitorState::Member_default_: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.default_ = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_testTaskState: + _pimpl->state = impl::VisitorState::Start; + if (const auto enumValue = internal::sorted_map_lookup(s_valuesTaskState, std::string_view { value })) + { + _pimpl->response.testTaskState = *enumValue; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Member_tasks_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Member_unreadCounts_edges_0_node_id: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->id = std::move(value); + break; + + case impl::VisitorState::Member_anyType_0_id: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->id = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_appointments_edges_0_node_isNow: + _pimpl->state = impl::VisitorState::Member_appointments_edges_0_node; + _pimpl->response.appointments.edges->back()->node->isNow = value; + break; + + case impl::VisitorState::Member_tasks_edges_0_node_isComplete: + _pimpl->state = impl::VisitorState::Member_tasks_edges_0_node; + _pimpl->response.tasks.edges->back()->node->isComplete = value; + break; + + case impl::VisitorState::Member_anyType_0_isComplete: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isComplete = value; + break; + + case impl::VisitorState::Member_anyType_0_isNow: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + _pimpl->response.anyType.back()->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_unreadCounts_edges_0_node_unreadCount: + _pimpl->state = impl::VisitorState::Member_unreadCounts_edges_0_node; + _pimpl->response.unreadCounts.edges->back()->node->unreadCount = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; diff --git a/samples/client/query/QueryClient.h b/samples/client/query/QueryClient.h index 57709b13..9f52a969 100644 --- a/samples/client/query/QueryClient.h +++ b/samples/client/query/QueryClient.h @@ -194,6 +194,37 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional default_ {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -203,6 +234,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = Query::Response; + using ResponseVisitor = Query::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; diff --git a/samples/client/query/QueryClient.ixx b/samples/client/query/QueryClient.ixx index 7d59ccdf..f069b326 100644 --- a/samples/client/query/QueryClient.ixx +++ b/samples/client/query/QueryClient.ixx @@ -31,6 +31,7 @@ using Query::GetOperationName; using graphql::query::TaskState; using Query::Response; +using Query::ResponseVisitor; using Query::parseResponse; using Query::Traits; diff --git a/samples/client/subscribe/SubscribeClient.cpp b/samples/client/subscribe/SubscribeClient.cpp index 3945722a..8ea64274 100644 --- a/samples/client/subscribe/SubscribeClient.cpp +++ b/samples/client/subscribe/SubscribeClient.cpp @@ -107,6 +107,316 @@ const std::string& GetOperationName() noexcept return s_name; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_nextAppointment, + Member_nextAppointment_nextAppointmentId, + Member_nextAppointment_when, + Member_nextAppointment_subject, + Member_nextAppointment_isNow, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.nextAppointment = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_nextAppointmentId: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->nextAppointmentId = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_when: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->when = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_subject: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->subject = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_nextAppointment_isNow: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->isNow = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->response.nextAppointment = std::make_optional({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "nextAppointment"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment; + } + break; + + case impl::VisitorState::Member_nextAppointment: + if (key == "nextAppointmentId"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_nextAppointmentId; + } + else if (key == "when"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_when; + } + else if (key == "subject"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_subject; + } + else if (key == "isNow"sv) + { + _pimpl->state = impl::VisitorState::Member_nextAppointment_isNow; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.nextAppointment = std::nullopt; + break; + + case impl::VisitorState::Member_nextAppointment_when: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->when = std::nullopt; + break; + + case impl::VisitorState::Member_nextAppointment_subject: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->subject = std::nullopt; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment_subject: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->subject = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment_nextAppointmentId: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->nextAppointmentId = std::move(value); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_nextAppointment_isNow: + _pimpl->state = impl::VisitorState::Member_nextAppointment; + _pimpl->response.nextAppointment->isNow = value; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; diff --git a/samples/client/subscribe/SubscribeClient.h b/samples/client/subscribe/SubscribeClient.h index a57fd5cd..de494e84 100644 --- a/samples/client/subscribe/SubscribeClient.h +++ b/samples/client/subscribe/SubscribeClient.h @@ -67,6 +67,37 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional nextAppointment {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -76,6 +107,7 @@ struct Traits [[nodiscard("unnecessary call")]] static const std::string& GetOperationName() noexcept; using Response = TestSubscription::Response; + using ResponseVisitor = TestSubscription::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; diff --git a/samples/client/subscribe/SubscribeClient.ixx b/samples/client/subscribe/SubscribeClient.ixx index 13456256..36dfdbca 100644 --- a/samples/client/subscribe/SubscribeClient.ixx +++ b/samples/client/subscribe/SubscribeClient.ixx @@ -23,6 +23,7 @@ using graphql::subscribe::client::GetRequestObject; using TestSubscription::GetOperationName; using TestSubscription::Response; +using TestSubscription::ResponseVisitor; using TestSubscription::parseResponse; using TestSubscription::Traits; diff --git a/samples/learn/schema/StarWarsSharedTypes.cpp b/samples/learn/schema/StarWarsSharedTypes.cpp index 3bfcae45..b1034fe2 100644 --- a/samples/learn/schema/StarWarsSharedTypes.cpp +++ b/samples/learn/schema/StarWarsSharedTypes.cpp @@ -50,11 +50,7 @@ service::AwaitableResolver Result::convert(service::AwaitableSca return ModifiedResult::resolve(std::move(result), std::move(params), [](learn::Episode value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesEpisode[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesEpisode[static_cast(value)] } } } }; }); } diff --git a/samples/proxy/query/ProxyClient.cpp b/samples/proxy/query/ProxyClient.cpp index d40a39a9..8a8362d8 100644 --- a/samples/proxy/query/ProxyClient.cpp +++ b/samples/proxy/query/ProxyClient.cpp @@ -87,6 +87,12 @@ response::Value Variable::serialize(QueryInput&& inputValue) return result; } +static const std::array, 3> s_valuesOperationType = { + std::make_pair(R"gql(QUERY)gql"sv, OperationType::QUERY), + std::make_pair(R"gql(MUTATION)gql"sv, OperationType::MUTATION), + std::make_pair(R"gql(SUBSCRIPTION)gql"sv, OperationType::SUBSCRIPTION) +}; + template <> OperationType Response::parse(response::Value&& value) { @@ -95,14 +101,8 @@ OperationType Response::parse(response::Value&& value) throw std::logic_error { R"ex(not a valid OperationType value)ex" }; } - static const std::array, 3> s_values = { - std::make_pair(R"gql(QUERY)gql"sv, OperationType::QUERY), - std::make_pair(R"gql(MUTATION)gql"sv, OperationType::MUTATION), - std::make_pair(R"gql(SUBSCRIPTION)gql"sv, OperationType::SUBSCRIPTION) - }; - const auto result = internal::sorted_map_lookup( - s_values, + s_valuesOperationType, std::string_view { value.get() }); if (!result) @@ -162,6 +162,299 @@ response::Value serializeVariables(Variables&& variables) return result; } +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, + Member_relay, + Member_relay_data, + Member_relay_errors, + Member_relay_errors_0, + Member_relay_errors_0_, + Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay: + _pimpl->state = impl::VisitorState::Start; + _pimpl->response.relay = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_relay_data: + _pimpl->state = impl::VisitorState::Member_relay; + _pimpl->response.relay.data = ModifiedResponse::parse(response::Value { *value }); + break; + + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->push_back(ModifiedResponse::parse(response::Value { *value })); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->reserve(count); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_errors_0: + _pimpl->state = impl::VisitorState::Member_relay_errors_0_; + _pimpl->response.relay.errors->push_back(std::make_optional({})); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Start: + if (key == "relay"sv) + { + _pimpl->state = impl::VisitorState::Member_relay; + } + break; + + case impl::VisitorState::Member_relay: + if (key == "data"sv) + { + _pimpl->state = impl::VisitorState::Member_relay_data; + } + else if (key == "errors"sv) + { + _pimpl->state = impl::VisitorState::Member_relay_errors; + } + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay: + _pimpl->state = impl::VisitorState::Start; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_errors: + _pimpl->state = impl::VisitorState::Member_relay_errors_0; + _pimpl->response.relay.errors = std::make_optional>>({}); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_errors_0: + _pimpl->state = impl::VisitorState::Member_relay; + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_data: + _pimpl->state = impl::VisitorState::Member_relay; + _pimpl->response.relay.data = std::nullopt; + break; + + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->push_back(std::nullopt); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Member_relay_data: + _pimpl->state = impl::VisitorState::Member_relay; + _pimpl->response.relay.data = std::move(value); + break; + + case impl::VisitorState::Member_relay_errors_0: + _pimpl->response.relay.errors->push_back(std::move(value)); + break; + + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::client; diff --git a/samples/proxy/query/ProxyClient.h b/samples/proxy/query/ProxyClient.h index 58669b8f..01c01ca4 100644 --- a/samples/proxy/query/ProxyClient.h +++ b/samples/proxy/query/ProxyClient.h @@ -76,6 +76,37 @@ struct [[nodiscard("unnecessary construction")]] Response relay_QueryResults relay {}; }; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -89,6 +120,7 @@ struct Traits [[nodiscard("unnecessary conversion")]] static response::Value serializeVariables(Variables&& variables); using Response = relayQuery::Response; + using ResponseVisitor = relayQuery::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; diff --git a/samples/proxy/query/ProxyClient.ixx b/samples/proxy/query/ProxyClient.ixx index d83227a1..51560939 100644 --- a/samples/proxy/query/ProxyClient.ixx +++ b/samples/proxy/query/ProxyClient.ixx @@ -38,6 +38,7 @@ using relayQuery::Variables; using relayQuery::serializeVariables; using relayQuery::Response; +using relayQuery::ResponseVisitor; using relayQuery::parseResponse; using relayQuery::Traits; diff --git a/samples/proxy/schema/ProxySharedTypes.cpp b/samples/proxy/schema/ProxySharedTypes.cpp index 66aafa00..8fba4aa3 100644 --- a/samples/proxy/schema/ProxySharedTypes.cpp +++ b/samples/proxy/schema/ProxySharedTypes.cpp @@ -50,11 +50,7 @@ service::AwaitableResolver Result::convert(service::Awaita return ModifiedResult::resolve(std::move(result), std::move(params), [](proxy::OperationType value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesOperationType[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesOperationType[static_cast(value)] } } } }; }); } diff --git a/samples/today/nointrospection/TodaySharedTypes.cpp b/samples/today/nointrospection/TodaySharedTypes.cpp index d3a0c4a0..2d9d50ca 100644 --- a/samples/today/nointrospection/TodaySharedTypes.cpp +++ b/samples/today/nointrospection/TodaySharedTypes.cpp @@ -50,11 +50,7 @@ service::AwaitableResolver Result::convert(service::AwaitableS return ModifiedResult::resolve(std::move(result), std::move(params), [](today::TaskState value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesTaskState[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesTaskState[static_cast(value)] } } } }; }); } diff --git a/samples/today/schema/TodaySharedTypes.cpp b/samples/today/schema/TodaySharedTypes.cpp index d3a0c4a0..2d9d50ca 100644 --- a/samples/today/schema/TodaySharedTypes.cpp +++ b/samples/today/schema/TodaySharedTypes.cpp @@ -50,11 +50,7 @@ service::AwaitableResolver Result::convert(service::AwaitableS return ModifiedResult::resolve(std::move(result), std::move(params), [](today::TaskState value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesTaskState[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesTaskState[static_cast(value)] } } } }; }); } diff --git a/samples/validation/schema/ValidationSharedTypes.cpp b/samples/validation/schema/ValidationSharedTypes.cpp index 47c18527..4db548fa 100644 --- a/samples/validation/schema/ValidationSharedTypes.cpp +++ b/samples/validation/schema/ValidationSharedTypes.cpp @@ -50,11 +50,7 @@ service::AwaitableResolver Result::convert(service::Awai return ModifiedResult::resolve(std::move(result), std::move(params), [](validation::DogCommand value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesDogCommand[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesDogCommand[static_cast(value)] } } } }; }); } @@ -106,11 +102,7 @@ service::AwaitableResolver Result::convert(service::Awai return ModifiedResult::resolve(std::move(result), std::move(params), [](validation::CatCommand value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesCatCommand[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesCatCommand[static_cast(value)] } } } }; }); } diff --git a/src/ClientGenerator.cpp b/src/ClientGenerator.cpp index 3b37757e..6f3de4a9 100644 --- a/src/ClientGenerator.cpp +++ b/src/ClientGenerator.cpp @@ -478,6 +478,37 @@ using graphql::)cpp" headerFile << R"cpp(}; +class ResponseVisitor + : public std::enable_shared_from_this +{ +public: + ResponseVisitor() noexcept; + ~ResponseVisitor(); + + void add_value(std::shared_ptr&&); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Response response(); + +private: + struct impl; + + std::unique_ptr _pimpl; +}; + [[nodiscard("unnecessary conversion")]] Response parseResponse(response::Value&& response); struct Traits @@ -500,6 +531,8 @@ struct Traits headerFile << R"cpp( using Response = )cpp" << _requestLoader.getOperationNamespace(operation) << R"cpp(::Response; + using ResponseVisitor = )cpp" + << _requestLoader.getOperationNamespace(operation) << R"cpp(::ResponseVisitor; [[nodiscard("unnecessary conversion")]] static Response parseResponse(response::Value&& response); }; @@ -821,6 +854,8 @@ using )cpp" << operationNamespace pendingSeparator.reset(); moduleFile << R"cpp(using )cpp" << operationNamespace << R"cpp(::Response; +using )cpp" << operationNamespace + << R"cpp(::ResponseVisitor; using )cpp" << operationNamespace << R"cpp(::parseResponse; @@ -1134,8 +1169,7 @@ using namespace )cpp" << _schemaLoader.getSchemaNamespace() sourceFile << R"cpp(template <> response::Value Variable<)cpp" - << cppType << R"cpp(>::serialize()cpp" << cppType - << R"cpp(&& inputValue) + << cppType << R"cpp(>::serialize()cpp" << cppType << R"cpp(&& inputValue) { response::Value result { response::Type::Map }; @@ -1186,19 +1220,9 @@ using namespace )cpp" << _schemaLoader.getSchemaNamespace() const auto& enumValues = enumType->enumValues(); - sourceFile << R"cpp(template <> -)cpp" << cppType << R"cpp( Response<)cpp" - << cppType << R"cpp(>::parse(response::Value&& value) -{ - if (!value.maybe_enum()) - { - throw std::logic_error { R"ex(not a valid )cpp" - << enumType->name() << R"cpp( value)ex" }; - } - - static const std::array, )cpp" << enumValues.size() - << R"cpp(> s_values = {)cpp"; + sourceFile << R"cpp(static const std::array, )cpp" << enumValues.size() << R"cpp(> s_values)cpp" << cppType + << R"cpp( = {)cpp"; std::vector> sortedValues( enumValues.size()); @@ -1224,17 +1248,28 @@ using namespace )cpp" << _schemaLoader.getSchemaNamespace() firstValue = false; sourceFile << R"cpp( - std::make_pair(R"gql()cpp" + std::make_pair(R"gql()cpp" << enumValue.first << R"cpp()gql"sv, )cpp" << cppType << R"cpp(::)cpp" << enumValue.second << R"cpp())cpp"; pendingSeparator.add(); } pendingSeparator.reset(); - sourceFile << R"cpp( }; + sourceFile << R"cpp(}; + +template <> +)cpp" << cppType << R"cpp( Response<)cpp" + << cppType << R"cpp(>::parse(response::Value&& value) +{ + if (!value.maybe_enum()) + { + throw std::logic_error { R"ex(not a valid )cpp" + << enumType->name() << R"cpp( value)ex" }; + } const auto result = internal::sorted_map_lookup( - s_values, + s_values)cpp" << cppType + << R"cpp(, std::string_view { value.get() }); if (!result) @@ -1316,6 +1351,324 @@ response::Value serializeVariables(Variables&& variables) } sourceFile << R"cpp( +struct ResponseVisitor::impl +{ + enum class VisitorState + { + Start, +)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorStates(sourceFile, responseField); + } + + sourceFile << R"cpp( Complete, + }; + + VisitorState state { VisitorState::Start }; + Response response {}; +}; + +ResponseVisitor::ResponseVisitor() noexcept + : _pimpl { std::make_unique() } +{ +} + +ResponseVisitor::~ResponseVisitor() +{ +} + +void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptr&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddValue(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorReserve(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_object() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorStartObject(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) +{ + switch (_pimpl->state) + {)cpp"; + + outputResponseFieldVisitorAddMember(sourceFile, responseType.fields); + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_object() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorEndObject(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::start_array() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorStartArray(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::end_array() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorEndArray(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_null() +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddNull(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_string([[maybe_unused]] std::string&& value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddString(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_enum([[maybe_unused]] std::string&& value) +{ + using namespace graphql::client; + + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddEnum(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddId(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_bool([[maybe_unused]] bool value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddBool(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_int([[maybe_unused]] int value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddInt(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::add_float([[maybe_unused]] double value) +{ + switch (_pimpl->state) + {)cpp"; + + for (const auto& responseField : responseType.fields) + { + outputResponseFieldVisitorAddFloat(sourceFile, responseField); + } + + sourceFile << R"cpp( + case impl::VisitorState::Complete: + break; + + default: + break; + } +} + +void ResponseVisitor::complete() +{ + _pimpl->state = impl::VisitorState::Complete; +} + +Response ResponseVisitor::response() +{ + Response response {}; + + switch (_pimpl->state) + { + case impl::VisitorState::Complete: + _pimpl->state = impl::VisitorState::Start; + std::swap(_pimpl->response, response); + break; + + default: + break; + } + + return response; +} + Response parseResponse(response::Value&& response) { using namespace graphql::)cpp" @@ -1581,6 +1934,1460 @@ std::string Generator::getTypeModifierList(const TypeModifierStack& modifiers) n return oss.str(); } +void Generator::outputResponseFieldVisitorStates(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parent /* = {} */) const noexcept +{ + auto state = + std::format("{}_{}", parent.empty() ? R"cpp(Member)cpp"sv : parent, responseField.cppName); + + sourceFile << R"cpp( )cpp" << state << R"cpp(, +)cpp"; + + std::size_t arrayDimensions = 0; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + state = std::format("{}_{}", state, arrayDimensions++); + sourceFile << R"cpp( )cpp" << state << R"cpp(, +)cpp"; + break; + } + } + + if (arrayDimensions > 0) + { + sourceFile << R"cpp( )cpp" << state << R"cpp(_, +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorStates(sourceFile, field, state); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddValue(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */, std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(ModifiedResponse<)cpp" << cppType << R"cpp(>::parse)cpp"; + + if (arrayDimensions > 0) + { + TypeModifierStack skippedModifiers; + + skippedModifiers.reserve(responseField.modifiers.size() - arrayDimensions); + std::ranges::copy(std::views::all(responseField.modifiers) | std::views::reverse + | std::views::take_while([](auto modifier) noexcept { + return modifier != service::TypeModifier::List; + }) + | std::views::reverse, + std::back_inserter(skippedModifiers)); + sourceFile << getTypeModifierList(skippedModifiers); + } + else + { + sourceFile << getTypeModifierList(responseField.modifiers); + } + + sourceFile << R"cpp((response::Value { *value }))cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddValue(sourceFile, + field, + arrayDimensions > 0, + state, + accessor, + cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorReserve(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + std::size_t arrayDimensions = 0; + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + state = std::format("{}_{}", state, arrayDimensions++); + + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(: + _pimpl->response.)cpp" + << accessor << R"cpp(reserve(count); + break; +)cpp"; + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorReserve(sourceFile, field, state, accessor, cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorStartObject(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (isNullable && arrayDimensions == 0) + { + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + break; + + default: + isNullable = false; + break; + } + } + + if (isNullable || arrayDimensions > 0) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << state << R"cpp(_;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + if (isNullable) + { + sourceFile << R"cpp(std::make_optional<)cpp" << cppType << R"cpp(>({}))cpp"; + } + else + { + sourceFile << R"cpp({})cpp"; + } + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorStartObject(sourceFile, + field, + state, + accessor, + cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddMember(std::ostream& sourceFile, + const ResponseFieldList& children, bool arrayElement /* = false */, + std::string_view parentState /* = {} */) const noexcept +{ + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << (parentState.empty() ? R"cpp(Start)cpp"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(: + )cpp"; + + std::unordered_set fieldNames; + bool firstField = true; + + for (const auto& field : children) + { + if (!fieldNames.emplace(field.name).second) + { + continue; + } + + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + field.cppName); + + if (!firstField) + { + sourceFile << R"cpp(else )cpp"; + } + + firstField = false; + + sourceFile << R"cpp(if (key == ")cpp" << field.name << R"cpp("sv) + { + _pimpl->state = impl::VisitorState::)cpp" + << state << R"cpp(; + } + )cpp"; + } + + sourceFile << R"cpp(break; +)cpp"; + + fieldNames.clear(); + + for (const auto& field : children) + { + switch (field.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + break; + + default: + continue; + } + + if (!fieldNames.emplace(field.name).second) + { + continue; + } + + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + field.cppName); + std::size_t arrayDimensions = 0; + + for (auto modifier : field.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + outputResponseFieldVisitorAddMember(sourceFile, field.children, arrayDimensions > 0, state); + } +} + +void Generator::outputResponseFieldVisitorEndObject(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */) const noexcept +{ + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + break; + + default: + return; + } + + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + + std::size_t arrayDimensions = 0; + std::string arrayState; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + std::unordered_set fieldNames; + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorEndObject(sourceFile, field, arrayDimensions > 0, state); + } + } + + if (arrayDimensions > 0) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(_: + _pimpl->state = impl::VisitorState::)cpp" + << state; + } + else + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(: + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + } + + sourceFile << R"cpp(; + break; +)cpp"; +} + +void Generator::outputResponseFieldVisitorStartArray(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool dereference = true; + std::size_t arrayDimensions = 0; + std::size_t skipModifiers = 0; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + dereference = false; + break; + + case service::TypeModifier::List: + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + state = std::format("{}_{}", state, arrayDimensions++); + + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << state << R"cpp(;)cpp"; + + if (!dereference) + { + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 1) + { + sourceFile << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + TypeModifierStack skippedModifiers; + + skippedModifiers.reserve(responseField.modifiers.size() - skipModifiers); + std::ranges::copy( + std::views::all(responseField.modifiers) | std::views::drop(skipModifiers), + std::back_inserter(skippedModifiers)); + + sourceFile << R"cpp(std::make_optional<)cpp" + << RequestLoader::getOutputCppType(cppType, skippedModifiers) + << R"cpp(>({}))cpp"; + + if (arrayDimensions > 1) + { + sourceFile << R"cpp();)cpp"; + } + else + { + sourceFile << R"cpp(;)cpp"; + } + } + + sourceFile << R"cpp( + break; +)cpp"; + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + else + { + accessor.append(R"cpp(->)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + + ++skipModifiers; + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorStartArray(sourceFile, + field, + state, + accessor, + cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorEndArray(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + + std::size_t arrayDimensions = 0; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + case service::TypeModifier::Nullable: + break; + + case service::TypeModifier::List: + { + auto child = std::format("{}_{}", state, arrayDimensions++); + std::string_view parent { state }; + + if (arrayDimensions == 1) + { + parent = parentState.empty() ? "Start"sv : parentState; + } + + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << child << R"cpp(: + _pimpl->state = impl::VisitorState::)cpp" + << parent; + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + + state = std::move(child); + break; + } + } + } + + std::unordered_set fieldNames; + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorEndArray(sourceFile, field, arrayDimensions > 0, state); + } + } +} + +void Generator::outputResponseFieldVisitorAddNull(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (isNullable) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(std::nullopt)cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddNull(sourceFile, + field, + arrayDimensions > 0, + state, + accessor); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddMovedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view movedCppType, + bool arrayElement /* = false */, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (getResponseFieldCppType(responseField) == movedCppType) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(std::move(value))cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddMovedValue(sourceFile, + field, + movedCppType, + arrayDimensions > 0, + state, + accessor); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddString( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddMovedValue(sourceFile, responseField, R"cpp(std::string)cpp"sv); +} + +void Generator::outputResponseFieldVisitorAddEnum(std::ostream& sourceFile, + const ResponseField& responseField, bool arrayElement /* = false */, + std::string_view parentState /* = {} */, std::string_view parentAccessor /* = {} */, + std::string_view parentCppType /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + auto cppType = getResponseFieldCppType(responseField, + parentCppType.empty() ? R"cpp(Response)cpp"sv : parentCppType); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (responseField.type->kind() == introspection::TypeKind::ENUM) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + if (const auto enumValue = internal::sorted_map_lookup(s_values)cpp" + << cppType << R"cpp(, std::string_view { value })) + { + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(*enumValue)cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + } + break; +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddEnum(sourceFile, + field, + arrayDimensions > 0, + state, + accessor, + cppType); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddId( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddMovedValue(sourceFile, + responseField, + R"cpp(response::IdType)cpp"sv); +} + +void Generator::outputResponseFieldVisitorAddCopiedValue(std::ostream& sourceFile, + const ResponseField& responseField, std::string_view copiedCppType, + bool arrayElement /* = false */, std::string_view parentState /* = {} */, + std::string_view parentAccessor /* = {} */) const noexcept +{ + auto state = std::format("{}_{}", + parentState.empty() ? R"cpp(Member)cpp"sv : parentState, + responseField.cppName); + auto accessor = std::format("{}{}", parentAccessor, responseField.cppName); + + bool isNullable = false; + std::size_t arrayDimensions = 0; + std::optional lastNullableDimension {}; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + isNullable = true; + break; + + case service::TypeModifier::List: + if (isNullable) + { + lastNullableDimension = arrayDimensions; + isNullable = false; + } + + state = std::format("{}_{}", state, arrayDimensions++); + break; + } + } + + if (getResponseFieldCppType(responseField) == copiedCppType) + { + sourceFile << R"cpp( + case impl::VisitorState::)cpp" + << state << R"cpp(:)cpp"; + + if (arrayDimensions == 0) + { + sourceFile << R"cpp( + _pimpl->state = impl::VisitorState::)cpp" + << (parentState.empty() ? "Start"sv : parentState); + + if (arrayElement) + { + sourceFile << R"cpp(_)cpp"; + } + + sourceFile << R"cpp(;)cpp"; + } + + sourceFile << R"cpp( + _pimpl->response.)cpp" + << accessor; + + if (arrayDimensions > 0) + { + sourceFile << ((lastNullableDimension && *lastNullableDimension + 1 == arrayDimensions) + ? R"cpp(->)cpp" + : R"cpp(.)cpp") + << R"cpp(push_back()cpp"; + } + else + { + sourceFile << R"cpp( = )cpp"; + } + + sourceFile << R"cpp(value)cpp"; + + if (arrayDimensions > 0) + { + sourceFile << R"cpp())cpp"; + } + + sourceFile << R"cpp(; + break; +)cpp"; + } + + std::unordered_set fieldNames; + + switch (responseField.type->kind()) + { + case introspection::TypeKind::OBJECT: + case introspection::TypeKind::INTERFACE: + case introspection::TypeKind::UNION: + { + bool dereference = true; + + for (auto modifier : responseField.modifiers) + { + switch (modifier) + { + case service::TypeModifier::None: + break; + + case service::TypeModifier::Nullable: + accessor.append(R"cpp(->)cpp"); + dereference = false; + break; + + case service::TypeModifier::List: + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + accessor.append(R"cpp(back())cpp"); + dereference = true; + break; + } + } + + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + + for (const auto& field : responseField.children) + { + if (fieldNames.emplace(field.name).second) + { + outputResponseFieldVisitorAddCopiedValue(sourceFile, + field, + copiedCppType, + arrayDimensions > 0, + state, + accessor); + } + } + + break; + } + + default: + break; + } +} + +void Generator::outputResponseFieldVisitorAddBool( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddCopiedValue(sourceFile, responseField, R"cpp(bool)cpp"sv); +} + +void Generator::outputResponseFieldVisitorAddInt( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddCopiedValue(sourceFile, responseField, R"cpp(int)cpp"sv); +} + +void Generator::outputResponseFieldVisitorAddFloat( + std::ostream& sourceFile, const ResponseField& responseField) const noexcept +{ + outputResponseFieldVisitorAddCopiedValue(sourceFile, responseField, R"cpp(double)cpp"sv); +} + } // namespace graphql::generator::client namespace po = boost::program_options; diff --git a/src/GraphQLResponse.cpp b/src/GraphQLResponse.cpp index d04a3bf3..984a2034 100644 --- a/src/GraphQLResponse.cpp +++ b/src/GraphQLResponse.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -100,9 +101,9 @@ bool IdType::operator==(const IdType& rhs) const noexcept return (std::holds_alternative(_data) ? internal::Base64::compareBase64(std::get(_data), - std::get(rhs._data)) + std::get(rhs._data)) : internal::Base64::compareBase64(std::get(rhs._data), - std::get(_data))) + std::get(_data))) == internal::Base64::Comparison::EqualTo; } @@ -145,7 +146,7 @@ bool IdType::operator<(const IdType& rhs) const noexcept return (std::holds_alternative(_data) ? (internal::Base64::compareBase64(std::get(_data), std::get(rhs._data)) - < internal::Base64::Comparison::EqualTo) + < internal::Base64::Comparison::EqualTo) : (internal::Base64::compareBase64(std::get(rhs._data), std::get(_data))) > internal::Base64::Comparison::EqualTo); @@ -1446,6 +1447,385 @@ const Value& Value::operator[](std::size_t index) const return std::get(typeData).at(index); } +void ValueVisitor::add_value(std::shared_ptr&& value) +{ + _concept->add_value(std::move(value)); +} + +void ValueVisitor::reserve(std::size_t count) +{ + _concept->reserve(count); +} + +void ValueVisitor::start_object() +{ + _concept->start_object(); +} + +void ValueVisitor::add_member(std::string&& key) +{ + _concept->add_member(std::move(key)); +} + +void ValueVisitor::end_object() +{ + _concept->end_object(); +} + +void ValueVisitor::start_array() +{ + _concept->start_array(); +} + +void ValueVisitor::end_array() +{ + _concept->end_array(); +} + +void ValueVisitor::add_null() +{ + _concept->add_null(); +} + +void ValueVisitor::add_string(std::string&& value) +{ + _concept->add_string(std::move(value)); +} + +void ValueVisitor::add_enum(std::string&& value) +{ + _concept->add_enum(std::move(value)); +} + +void ValueVisitor::add_id(response::IdType&& value) +{ + _concept->add_id(std::move(value)); +} + +void ValueVisitor::add_bool(bool value) +{ + _concept->add_bool(value); +} + +void ValueVisitor::add_int(int value) +{ + _concept->add_int(value); +} + +void ValueVisitor::add_float(double value) +{ + _concept->add_float(value); +} + +void ValueVisitor::complete() +{ + _concept->complete(); +} + +ValueToken::ValueToken(OpaqueValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(Reserve&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(StartObject&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(AddMember&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(EndObject&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(StartArray&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(EndArray&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(NullValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(StringValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(EnumValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(IdValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(BoolValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(IntValue&& value) + : _value { std::move(value) } +{ +} + +ValueToken::ValueToken(FloatValue&& value) + : _value { std::move(value) } +{ +} + +void ValueToken::visit(const std::shared_ptr& visitor) && +{ + std::visit( + [&visitor](auto&& value) { + using value_type = std::decay_t; + + if constexpr (std::is_same_v) + { + visitor->add_value(std::move(value)); + } + else if constexpr (std::is_same_v) + { + visitor->reserve(value.capacity); + } + else if constexpr (std::is_same_v) + { + visitor->start_object(); + } + else if constexpr (std::is_same_v) + { + visitor->add_member(std::move(value.key)); + } + else if constexpr (std::is_same_v) + { + visitor->end_object(); + } + else if constexpr (std::is_same_v) + { + visitor->start_array(); + } + else if constexpr (std::is_same_v) + { + visitor->end_array(); + } + else if constexpr (std::is_same_v) + { + visitor->add_null(); + } + else if constexpr (std::is_same_v) + { + visitor->add_string(std::move(value.value)); + } + else if constexpr (std::is_same_v) + { + visitor->add_enum(std::move(value.value)); + } + else if constexpr (std::is_same_v) + { + visitor->add_id(std::move(value.value)); + } + else if constexpr (std::is_same_v) + { + visitor->add_bool(value.value); + } + else if constexpr (std::is_same_v) + { + visitor->add_int(value.value); + } + else if constexpr (std::is_same_v) + { + visitor->add_float(value.value); + } + }, + std::move(_value)); +} + +class ValueTokenStreamVisitor +{ +public: + void add_value(std::shared_ptr&& value); + void reserve(std::size_t count); + void start_object(); + void add_member(std::string&& key); + void end_object(); + void start_array(); + void end_array(); + void add_null(); + void add_string(std::string&& value); + void add_enum(std::string&& value); + void add_id(response::IdType&& value); + void add_bool(bool value); + void add_int(int value); + void add_float(double value); + void complete(); + + Value value(); + +private: + void add_value(Value&& value); + + Value _result {}; + std::stack _values {}; + std::stack _keys {}; +}; + +void ValueTokenStreamVisitor::add_value(std::shared_ptr&& value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::reserve(std::size_t count) +{ + _values.top().reserve(count); +} + +void ValueTokenStreamVisitor::start_object() +{ + _values.push(Value { response::Type::Map }); +} + +void ValueTokenStreamVisitor::add_member(std::string&& key) +{ + _keys.push(std::move(key)); +} + +void ValueTokenStreamVisitor::end_object() +{ + auto value = std::move(_values.top()); + + _values.pop(); + add_value(std::move(value)); +} + +void ValueTokenStreamVisitor::start_array() +{ + _values.push(Value { response::Type::List }); +} + +void ValueTokenStreamVisitor::end_array() +{ + auto value = std::move(_values.top()); + + _values.pop(); + add_value(std::move(value)); +} + +void ValueTokenStreamVisitor::add_null() +{ + add_value(Value {}); +} + +void ValueTokenStreamVisitor::add_string(std::string&& value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_enum(std::string&& value) +{ + Value enumValue { response::Type::EnumValue }; + + enumValue.set(std::move(value)); + add_value(std::move(enumValue)); +} + +void ValueTokenStreamVisitor::add_id(response::IdType&& value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_bool(bool value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_int(int value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::add_float(double value) +{ + add_value(Value { std::move(value) }); +} + +void ValueTokenStreamVisitor::complete() +{ +} + +Value ValueTokenStreamVisitor::value() +{ + auto value = std::move(_result); + + return value; +} + +void ValueTokenStreamVisitor::add_value(Value&& value) +{ + if (_values.empty()) + { + _result = std::move(value); + return; + } + + switch (_values.top().type()) + { + case response::Type::Map: + _values.top().emplace_back(std::move(_keys.top()), std::move(value)); + _keys.pop(); + break; + + case response::Type::List: + _values.top().emplace_back(std::move(value)); + break; + + default: + throw std::logic_error("Invalid call to Value::emplace_back"); + break; + } +} + +void ValueTokenStream::append(ValueTokenStream&& other) +{ + _tokens.splice(_tokens.end(), std::move(other._tokens)); +} + +void ValueTokenStream::visit(const std::shared_ptr& visitor) && +{ + for (auto& token : _tokens) + { + std::move(token).visit(visitor); + } + + visitor->complete(); +} + +Value ValueTokenStream::value() && +{ + auto visitor = std::make_shared(); + + std::move(*this).visit(std::make_shared(visitor)); + + return visitor->value(); +} + void Writer::write(Value response) const { switch (response.type()) diff --git a/src/GraphQLService.cpp b/src/GraphQLService.cpp index 63bc919a..b7df2161 100644 --- a/src/GraphQLService.cpp +++ b/src/GraphQLService.cpp @@ -11,6 +11,7 @@ #include #include #include +#include using namespace std::literals; @@ -620,6 +621,20 @@ schema_location ResolverParams::getLocation() const return { position.line, position.column }; } +response::Value ResolverResult::document() && +{ + response::Value document { response::Type::Map }; + + document.emplace_back(std::string { strData }, std::move(data).value()); + + if (!errors.empty()) + { + document.emplace_back(std::string { strErrors }, buildErrorValues(std::move(errors))); + } + + return document; +} + template <> int Argument::convert(const response::Value& value) { @@ -703,7 +718,7 @@ AwaitableResolver Result::convert(AwaitableScalar result, ResolverPara return ModifiedResult::resolve(std::move(result), std::move(params), [](int&& value, const ResolverParams&) { - return response::Value(value); + return ResolverResult { { response::ValueToken::IntValue { value } } }; }); } @@ -715,7 +730,7 @@ AwaitableResolver Result::convert(AwaitableScalar result, Resolv return ModifiedResult::resolve(std::move(result), std::move(params), [](double&& value, const ResolverParams&) { - return response::Value(value); + return ResolverResult { { response::ValueToken::FloatValue { value } } }; }); } @@ -728,7 +743,7 @@ AwaitableResolver Result::convert( return ModifiedResult::resolve(std::move(result), std::move(params), [](std::string&& value, const ResolverParams&) { - return response::Value(std::move(value)); + return ResolverResult { { response::ValueToken::StringValue { std::move(value) } } }; }); } @@ -740,7 +755,7 @@ AwaitableResolver Result::convert(AwaitableScalar result, ResolverPa return ModifiedResult::resolve(std::move(result), std::move(params), [](bool&& value, const ResolverParams&) { - return response::Value(value); + return ResolverResult { { response::ValueToken::BoolValue { value } } }; }); } @@ -753,7 +768,8 @@ AwaitableResolver Result::convert( return ModifiedResult::resolve(std::move(result), std::move(params), [](response::Value&& value, const ResolverParams&) { - return response::Value(std::move(value)); + return ResolverResult { { response::ValueToken::OpaqueValue { + std::make_shared(std::move(value)) } } }; }); } @@ -766,7 +782,7 @@ AwaitableResolver Result::convert( return ModifiedResult::resolve(std::move(result), std::move(params), [](response::IdType&& value, const ResolverParams&) { - return response::Value(std::move(value)); + return ResolverResult { { response::ValueToken::IdValue { std::move(value) } } }; }); } @@ -799,7 +815,7 @@ AwaitableResolver Result::convert( if (!awaitedResult) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto document = co_await awaitedResult->resolve(params, @@ -1228,9 +1244,10 @@ AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, auto children = visitor.getValues(); const auto launch = selectionSetParams.launch; - ResolverResult document { response::Value { response::Type::Map } }; + ResolverResult document {}; - document.data.reserve(children.size()); + document.data.push_back(response::ValueToken::StartObject {}); + document.data.push_back(response::ValueToken::Reserve { children.size() }); const auto parent = selectionSetParams.errorPath ? std::make_optional(std::cref(*selectionSetParams.errorPath)) @@ -1244,19 +1261,12 @@ AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, auto value = co_await std::move(child.result); - if (!document.data.emplace_back(std::string { child.name }, std::move(value.data))) - { - auto message = std::format("Ambiguous field error name: {}", child.name); - field_path path { parent, path_segment { child.name } }; - - document.errors.push_back({ std::move(message), - child.location.value_or(schema_location {}), - buildErrorPath(std::make_optional(path)) }); - } + document.data.push_back(response::ValueToken::AddMember { std::string { child.name } }); + document.data.append(std::move(value.data)); if (!value.errors.empty()) { - document.errors.splice(document.errors.end(), value.errors); + document.errors.splice(document.errors.end(), std::move(value.errors)); } } catch (schema_exception& scx) @@ -1268,7 +1278,8 @@ AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, std::ranges::copy(errors, std::back_inserter(document.errors)); } - document.data.emplace_back(std::string { child.name }, {}); + document.data.push_back(response::ValueToken::AddMember { std::string { child.name } }); + document.data.push_back(response::ValueToken::NullValue {}); } catch (const std::exception& ex) { @@ -1279,10 +1290,13 @@ AwaitableResolver Object::resolve(const SelectionSetParams& selectionSetParams, document.errors.push_back({ std::move(message), child.location.value_or(schema_location {}), buildErrorPath(std::make_optional(path)) }); - document.data.emplace_back(std::string { child.name }, {}); + document.data.push_back(response::ValueToken::AddMember { std::string { child.name } }); + document.data.push_back(response::ValueToken::NullValue {}); } } + document.data.push_back(response::ValueToken::EndObject {}); + co_return std::move(document); } @@ -1380,7 +1394,7 @@ AwaitableResolver OperationDefinitionVisitor::getValue() { if (!_result) { - co_return ResolverResult {}; + co_return ResolverResult { { response::ValueToken::NullValue {} } }; } auto result = std::move(*_result); @@ -1461,7 +1475,8 @@ void OperationDefinitionVisitor::visit( SubscriptionData::SubscriptionData(std::shared_ptr data, SubscriptionName&& field, response::Value arguments, Directives fieldDirectives, peg::ast&& query, - std::string&& operationName, SubscriptionCallback&& callback, const peg::ast_node& selection) + std::string&& operationName, SubscriptionCallbackOrVisitor&& callback, + const peg::ast_node& selection) : data(std::move(data)) , field(std::move(field)) , arguments(std::move(arguments)) @@ -1758,6 +1773,11 @@ std::pair Request::findOperationDefiniti } response::AwaitableValue Request::resolve(RequestResolveParams params) const +{ + co_return (co_await visit(std::move(params))).document(); +} + +AwaitableResolver Request::visit(RequestResolveParams params) const { try { @@ -1814,27 +1834,11 @@ response::AwaitableValue Request::resolve(RequestResolveParams params) const co_await params.launch; operationVisitor.visit(operationType, *operationDefinition); - auto result = co_await operationVisitor.getValue(); - response::Value document { response::Type::Map }; - - document.emplace_back(std::string { strData }, std::move(result.data)); - - if (!result.errors.empty()) - { - document.emplace_back(std::string { strErrors }, - buildErrorValues(std::move(result.errors))); - } - - co_return std::move(document); + co_return co_await operationVisitor.getValue(); } catch (schema_exception& ex) { - response::Value document(response::Type::Map); - - document.emplace_back(std::string { strData }, response::Value()); - document.emplace_back(std::string { strErrors }, ex.getErrors()); - - co_return std::move(document); + co_return { {}, ex.getStructuredErrors() }; } } @@ -1995,32 +1999,36 @@ AwaitableDeliver Request::deliver(RequestDeliverParams params) const params.launch, }; - response::Value document { response::Type::Map }; + ResolverResult document {}; try { co_await params.launch; - auto result = co_await optionalOrDefaultSubscription->resolve(selectionSetParams, + document = co_await optionalOrDefaultSubscription->resolve(selectionSetParams, registration->selection, registration->data->fragments, registration->data->variables); - - document.emplace_back(std::string { strData }, std::move(result.data)); - - if (!result.errors.empty()) - { - document.emplace_back(std::string { strErrors }, - buildErrorValues(std::move(result.errors))); - } } catch (schema_exception& ex) { - document.emplace_back(std::string { strData }, response::Value()); - document.emplace_back(std::string { strErrors }, ex.getErrors()); + document.errors.splice(document.errors.end(), ex.getStructuredErrors()); } - registration->callback(std::move(document)); + std::visit( + [result = std::move(document)](const auto& callback) mutable { + using callback_type = std::decay_t; + + if constexpr (std::is_same_v) + { + callback(std::move(result).document()); + } + else if constexpr (std::is_same_v) + { + callback(std::move(result)); + } + }, + registration->callback); } co_return; diff --git a/src/SchemaGenerator.cpp b/src/SchemaGenerator.cpp index 1a62684a..38a6d248 100644 --- a/src/SchemaGenerator.cpp +++ b/src/SchemaGenerator.cpp @@ -1685,12 +1685,8 @@ service::AwaitableResolver Result<)cpp" []()cpp" << _loader.getSchemaNamespace() << R"cpp(::)cpp" << enumType.cppType << R"cpp( value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_names)cpp" - << enumType.cppType << R"cpp([static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_names)cpp" + << enumType.cppType << R"cpp([static_cast(value)] } } } }; }); } @@ -2492,8 +2488,7 @@ Operations::Operations()cpp"; )cpp"; } sourceFile << R"cpp(}, )cpp" - << (directive.isRepeatable ? R"cpp(true)cpp" : R"cpp(false)cpp") - << R"cpp()); + << (directive.isRepeatable ? R"cpp(true)cpp" : R"cpp(false)cpp") << R"cpp()); )cpp"; } } diff --git a/src/introspection/IntrospectionSharedTypes.cpp b/src/introspection/IntrospectionSharedTypes.cpp index 5b1892fe..ba9a3e7e 100644 --- a/src/introspection/IntrospectionSharedTypes.cpp +++ b/src/introspection/IntrospectionSharedTypes.cpp @@ -50,11 +50,7 @@ service::AwaitableResolver Result::convert(service::Awa return ModifiedResult::resolve(std::move(result), std::move(params), [](introspection::TypeKind value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesTypeKind[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesTypeKind[static_cast(value)] } } } }; }); } @@ -106,11 +102,7 @@ service::AwaitableResolver Result::convert(ser return ModifiedResult::resolve(std::move(result), std::move(params), [](introspection::DirectiveLocation value, const ResolverParams&) { - response::Value resolvedResult(response::Type::EnumValue); - - resolvedResult.set(std::string { s_namesDirectiveLocation[static_cast(value)] }); - - return resolvedResult; + return ResolverResult { { response::ValueToken::EnumValue { std::string { s_namesDirectiveLocation[static_cast(value)] } } } }; }); } diff --git a/test/ClientTests.cpp b/test/ClientTests.cpp index 031e4c47..da28230b 100644 --- a/test/ClientTests.cpp +++ b/test/ClientTests.cpp @@ -137,6 +137,106 @@ TEST_F(ClientCase, QueryEverything) } } +TEST_F(ClientCase, QueryEverythingWithVisitor) +{ + using namespace query::client::query::Query; + + auto query = GetRequestObject(); + + response::Value variables(response::Type::Map); + auto state = std::make_shared(1); + auto result = + _mockService->service->visit({ query, {}, std::move(variables), std::launch::async, state }) + .get(); + EXPECT_EQ(std::size_t { 1 }, _mockService->getAppointmentsCount) + << "today service lazy loads the appointments and caches the result"; + EXPECT_EQ(std::size_t { 1 }, _mockService->getTasksCount) + << "today service lazy loads the tasks and caches the result"; + EXPECT_EQ(std::size_t { 1 }, _mockService->getUnreadCountsCount) + << "today service lazy loads the unreadCounts and caches the result"; + EXPECT_EQ(std::size_t { 1 }, state->appointmentsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->tasksRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->unreadCountsRequestId) + << "today service passed the same RequestState"; + EXPECT_EQ(std::size_t { 1 }, state->loadAppointmentsCount) + << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadTasksCount) << "today service called the loader once"; + EXPECT_EQ(std::size_t { 1 }, state->loadUnreadCountsCount) + << "today service called the loader once"; + + try + { + auto visitor = std::make_shared(); + auto responseVisitor = std::make_shared(visitor); + std::move(result.data).visit(responseVisitor); + const auto response = visitor->response(); + + EXPECT_EQ(std::size_t { 0 }, result.errors.size()) << "no errors expected"; + + ASSERT_TRUE(response.appointments.edges.has_value()) << "appointments should be set"; + ASSERT_EQ(std::size_t { 1 }, response.appointments.edges->size()) + << "appointments should have 1 entry"; + ASSERT_TRUE((*response.appointments.edges)[0].has_value()) << "edge should be set"; + const auto& appointmentNode = (*response.appointments.edges)[0]->node; + ASSERT_TRUE(appointmentNode.has_value()) << "node should be set"; + EXPECT_EQ(today::getFakeAppointmentId(), appointmentNode->id) + << "id should match in base64 encoding"; + ASSERT_TRUE(appointmentNode->subject.has_value()) << "subject should be set"; + EXPECT_EQ("Lunch?", *(appointmentNode->subject)) << "subject should match"; + ASSERT_TRUE(appointmentNode->when.has_value()) << "when should be set"; + EXPECT_EQ("tomorrow", appointmentNode->when->get()) << "when should match"; + EXPECT_FALSE(appointmentNode->isNow) << "isNow should match"; + EXPECT_EQ("Appointment", appointmentNode->_typename) << "__typename should match"; + + ASSERT_TRUE(response.tasks.edges.has_value()) << "tasks should be set"; + ASSERT_EQ(std::size_t { 1 }, response.tasks.edges->size()) << "tasks should have 1 entry"; + ASSERT_TRUE((*response.tasks.edges)[0].has_value()) << "edge should be set"; + const auto& taskNode = (*response.tasks.edges)[0]->node; + ASSERT_TRUE(taskNode.has_value()) << "node should be set"; + EXPECT_EQ(today::getFakeTaskId(), taskNode->id) << "id should match in base64 encoding"; + ASSERT_TRUE(taskNode->title.has_value()) << "subject should be set"; + EXPECT_EQ("Don't forget", *(taskNode->title)) << "title should match"; + EXPECT_TRUE(taskNode->isComplete) << "isComplete should match"; + EXPECT_EQ("Task", taskNode->_typename) << "__typename should match"; + + ASSERT_TRUE(response.unreadCounts.edges.has_value()) << "unreadCounts should be set"; + ASSERT_EQ(std::size_t { 1 }, response.unreadCounts.edges->size()) + << "unreadCounts should have 1 entry"; + ASSERT_TRUE((*response.unreadCounts.edges)[0].has_value()) << "edge should be set"; + const auto& unreadCountNode = (*response.unreadCounts.edges)[0]->node; + ASSERT_TRUE(unreadCountNode.has_value()) << "node should be set"; + EXPECT_EQ(today::getFakeFolderId(), unreadCountNode->id) + << "id should match in base64 encoding"; + ASSERT_TRUE(unreadCountNode->name.has_value()) << "name should be set"; + EXPECT_EQ("\"Fake\" Inbox", *(unreadCountNode->name)) << "name should match"; + EXPECT_EQ(3, unreadCountNode->unreadCount) << "unreadCount should match"; + EXPECT_EQ("Folder", unreadCountNode->_typename) << "__typename should match"; + + EXPECT_EQ(query::client::query::Query::TaskState::Unassigned, response.testTaskState) + << "testTaskState should match"; + + ASSERT_EQ(std::size_t { 1 }, response.anyType.size()) << "anyType should have 1 entry"; + ASSERT_TRUE(response.anyType[0].has_value()) << "appointment should be set"; + const auto& anyType = *response.anyType[0]; + EXPECT_EQ("Appointment", anyType._typename) << "__typename should match"; + EXPECT_EQ(today::getFakeAppointmentId(), anyType.id) + << "id should match in base64 encoding"; + EXPECT_FALSE(anyType.title.has_value()) << "appointment should not have a title"; + EXPECT_FALSE(anyType.isComplete) << "appointment should not set isComplete"; + ASSERT_TRUE(anyType.subject.has_value()) << "subject should be set"; + EXPECT_EQ("Lunch?", *(anyType.subject)) << "subject should match"; + ASSERT_TRUE(anyType.when.has_value()) << "when should be set"; + EXPECT_EQ("tomorrow", anyType.when->get()) << "when should match"; + EXPECT_FALSE(anyType.isNow) << "isNow should match"; + } + catch (const std::logic_error& ex) + { + FAIL() << ex.what(); + } +} + TEST_F(ClientCase, MutateCompleteTask) { using namespace mutate::client::mutation::CompleteTaskMutation;