diff --git a/PEGTL b/PEGTL index cf639f7f..be527327 160000 --- a/PEGTL +++ b/PEGTL @@ -1 +1 @@ -Subproject commit cf639f7f4ee125f68e1ccfba8d99ebc0de57b9fe +Subproject commit be527327653e94b02e711f7eff59285ad13e1db0 diff --git a/samples/CMakeLists.txt b/samples/CMakeLists.txt index b3d479d7..9b86c5af 100644 --- a/samples/CMakeLists.txt +++ b/samples/CMakeLists.txt @@ -3,17 +3,10 @@ cmake_minimum_required(VERSION 3.28) -if(GRAPHQL_BUILD_MODULES) - add_subdirectory(today) -endif() - +add_subdirectory(today) add_subdirectory(client) add_subdirectory(learn) - -if(GRAPHQL_BUILD_MODULES) - add_subdirectory(stitched) -endif() - +add_subdirectory(stitched) add_subdirectory(validation) if(GRAPHQL_BUILD_HTTP_SAMPLE) diff --git a/samples/client/query/QueryClient.cpp b/samples/client/query/QueryClient.cpp index e6d9d180..934d4593 100644 --- a/samples/client/query/QueryClient.cpp +++ b/samples/client/query/QueryClient.cpp @@ -78,6 +78,7 @@ const std::string& GetRequestText() noexcept subject when isNow + array } } @@ -431,6 +432,11 @@ graphql::query::client::query::Query::Response::anyType_UnionType Response::parse(std::move(member.second)); continue; } + if (member.first == R"js(array)js"sv) + { + result.array = ModifiedResponse::parse(std::move(member.second)); + continue; + } } } @@ -492,6 +498,9 @@ struct ResponseVisitor::impl Member_anyType_0_subject, Member_anyType_0_when, Member_anyType_0_isNow, + Member_anyType_0_array, + Member_anyType_0_array_0, + Member_anyType_0_array_0_, Member_default_, Complete, }; @@ -666,6 +675,10 @@ void ResponseVisitor::add_value([[maybe_unused]] std::shared_ptrresponse.anyType.back()->isNow = ModifiedResponse::parse(response::Value { *value }); break; + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->response.anyType.back()->array.push_back(ModifiedResponse::parse(response::Value { *value })); + break; + case impl::VisitorState::Member_default_: _pimpl->state = impl::VisitorState::Start; _pimpl->response.default_ = ModifiedResponse::parse(response::Value { *value }); @@ -699,6 +712,10 @@ void ResponseVisitor::reserve([[maybe_unused]] std::size_t count) _pimpl->response.anyType.reserve(count); break; + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->response.anyType.back()->array.reserve(count); + break; + case impl::VisitorState::Complete: break; @@ -914,6 +931,10 @@ void ResponseVisitor::add_member([[maybe_unused]] std::string&& key) { _pimpl->state = impl::VisitorState::Member_anyType_0_isNow; } + else if (key == "array"sv) + { + _pimpl->state = impl::VisitorState::Member_anyType_0_array; + } break; case impl::VisitorState::Complete: @@ -999,6 +1020,10 @@ void ResponseVisitor::start_array() _pimpl->state = impl::VisitorState::Member_anyType_0; break; + case impl::VisitorState::Member_anyType_0_array: + _pimpl->state = impl::VisitorState::Member_anyType_0_array_0; + break; + case impl::VisitorState::Complete: break; @@ -1027,6 +1052,10 @@ void ResponseVisitor::end_array() _pimpl->state = impl::VisitorState::Start; break; + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->state = impl::VisitorState::Member_anyType_0_; + break; + case impl::VisitorState::Complete: break; @@ -1226,6 +1255,10 @@ void ResponseVisitor::add_id([[maybe_unused]] response::IdType&& value) _pimpl->response.anyType.back()->id = std::move(value); break; + case impl::VisitorState::Member_anyType_0_array_0: + _pimpl->response.anyType.back()->array.push_back(std::move(value)); + break; + case impl::VisitorState::Complete: break; diff --git a/samples/client/query/QueryClient.h b/samples/client/query/QueryClient.h index 9f52a969..36f7798e 100644 --- a/samples/client/query/QueryClient.h +++ b/samples/client/query/QueryClient.h @@ -81,6 +81,7 @@ namespace graphql::query { /// subject /// when /// isNow +/// array /// } /// } /// @@ -184,6 +185,7 @@ struct [[nodiscard("unnecessary construction")]] Response std::optional subject {}; std::optional when {}; bool isNow {}; + std::vector array {}; }; appointments_AppointmentConnection appointments {}; diff --git a/samples/client/query/query.today.graphql b/samples/client/query/query.today.graphql index 56bef0a3..32217e9c 100644 --- a/samples/client/query/query.today.graphql +++ b/samples/client/query/query.today.graphql @@ -53,6 +53,7 @@ query { subject when isNow + array } } diff --git a/samples/proxy/query/ProxyClient.cpp b/samples/proxy/query/ProxyClient.cpp index 8a8362d8..14d71d52 100644 --- a/samples/proxy/query/ProxyClient.cpp +++ b/samples/proxy/query/ProxyClient.cpp @@ -236,11 +236,6 @@ 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; diff --git a/samples/today/CMakeLists.txt b/samples/today/CMakeLists.txt index 9b6e1a6e..1ed024a7 100644 --- a/samples/today/CMakeLists.txt +++ b/samples/today/CMakeLists.txt @@ -37,49 +37,51 @@ if(MSVC) target_compile_options(todaygraphql_nointrospection PUBLIC /wd4702) endif() -# sample -add_executable(sample sample.cpp) -target_link_libraries(sample PRIVATE - todaygraphql - graphqljson) - -# sample_nointrospection -add_executable(sample_nointrospection sample.cpp) -target_link_libraries(sample_nointrospection PRIVATE - todaygraphql_nointrospection - graphqljson) - -# benchmark -add_executable(benchmark benchmark.cpp) -target_link_libraries(benchmark PRIVATE - todaygraphql - graphqljson) - -# benchmark_nointrospection -add_executable(benchmark_nointrospection benchmark.cpp) -target_link_libraries(benchmark_nointrospection PRIVATE - todaygraphql_nointrospection - graphqljson) - -if(WIN32 AND BUILD_SHARED_LIBS) - add_custom_command(OUTPUT copied_sample_dlls - COMMAND ${CMAKE_COMMAND} -E copy_if_different - $ - $ - $ - $ - ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E touch copied_sample_dlls - DEPENDS - graphqlservice - graphqljson - graphqlpeg - graphqlresponse) - - add_custom_target(copy_today_sample_dlls DEPENDS copied_sample_dlls) +if(GRAPHQL_BUILD_MODULES) + # sample + add_executable(sample sample.cpp) + target_link_libraries(sample PRIVATE + todaygraphql + graphqljson) - add_dependencies(sample copy_today_sample_dlls) - add_dependencies(sample_nointrospection copy_today_sample_dlls) - add_dependencies(benchmark copy_today_sample_dlls) - add_dependencies(benchmark_nointrospection copy_today_sample_dlls) + # sample_nointrospection + add_executable(sample_nointrospection sample.cpp) + target_link_libraries(sample_nointrospection PRIVATE + todaygraphql_nointrospection + graphqljson) + + # benchmark + add_executable(benchmark benchmark.cpp) + target_link_libraries(benchmark PRIVATE + todaygraphql + graphqljson) + + # benchmark_nointrospection + add_executable(benchmark_nointrospection benchmark.cpp) + target_link_libraries(benchmark_nointrospection PRIVATE + todaygraphql_nointrospection + graphqljson) + + if(WIN32 AND BUILD_SHARED_LIBS) + add_custom_command(OUTPUT copied_sample_dlls + COMMAND ${CMAKE_COMMAND} -E copy_if_different + $ + $ + $ + $ + ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E touch copied_sample_dlls + DEPENDS + graphqlservice + graphqljson + graphqlpeg + graphqlresponse) + + add_custom_target(copy_today_sample_dlls DEPENDS copied_sample_dlls) + + add_dependencies(sample copy_today_sample_dlls) + add_dependencies(sample_nointrospection copy_today_sample_dlls) + add_dependencies(benchmark copy_today_sample_dlls) + add_dependencies(benchmark_nointrospection copy_today_sample_dlls) + endif() endif() diff --git a/samples/today/TodayMock.cpp b/samples/today/TodayMock.cpp index 62d06c50..c05834d7 100644 --- a/samples/today/TodayMock.cpp +++ b/samples/today/TodayMock.cpp @@ -186,6 +186,11 @@ std::optional Appointment::getForceError() const throw std::runtime_error(R"ex(this error was forced)ex"); } +std::vector Appointment::getArray() const +{ + return {}; +} + AppointmentEdge::AppointmentEdge(std::shared_ptr appointment) : _appointment(std::move(appointment)) { diff --git a/samples/today/TodayMock.h b/samples/today/TodayMock.h index 3ccf15b1..6709cba9 100644 --- a/samples/today/TodayMock.h +++ b/samples/today/TodayMock.h @@ -146,6 +146,7 @@ class Appointment std::shared_ptr getSubject() const noexcept; bool getIsNow() const noexcept; std::optional getForceError() const; + std::vector getArray() const; private: response::IdType _id; diff --git a/samples/today/nointrospection/AppointmentObject.cpp b/samples/today/nointrospection/AppointmentObject.cpp index 39f8910e..ada849cd 100644 --- a/samples/today/nointrospection/AppointmentObject.cpp +++ b/samples/today/nointrospection/AppointmentObject.cpp @@ -39,6 +39,7 @@ service::ResolverMap Appointment::getResolvers() const noexcept return { { R"gql(id)gql"sv, [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } }, { R"gql(when)gql"sv, [this](service::ResolverParams&& params) { return resolveWhen(std::move(params)); } }, + { R"gql(array)gql"sv, [this](service::ResolverParams&& params) { return resolveArray(std::move(params)); } }, { R"gql(isNow)gql"sv, [this](service::ResolverParams&& params) { return resolveIsNow(std::move(params)); } }, { R"gql(subject)gql"sv, [this](service::ResolverParams&& params) { return resolveSubject(std::move(params)); } }, { R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } }, @@ -111,6 +112,17 @@ service::AwaitableResolver Appointment::resolveForceError(service::ResolverParam return service::ModifiedResult::convert(std::move(result), std::move(params)); } +service::AwaitableResolver Appointment::resolveArray(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getArray(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + service::AwaitableResolver Appointment::resolve_typename(service::ResolverParams&& params) const { return service::Result::convert(std::string{ R"gql(Appointment)gql" }, std::move(params)); @@ -128,7 +140,8 @@ void AddAppointmentDetails(const std::shared_ptr& typeAppoin schema::Field::Make(R"gql(when)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(DateTime)gql"sv)), schema::Field::Make(R"gql(subject)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), schema::Field::Make(R"gql(isNow)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Boolean)gql"sv))), - schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)) + schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), + schema::Field::Make(R"gql(array)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv))))) }); } diff --git a/samples/today/nointrospection/TodayAppointmentObject.h b/samples/today/nointrospection/TodayAppointmentObject.h index af90d938..7507a086 100644 --- a/samples/today/nointrospection/TodayAppointmentObject.h +++ b/samples/today/nointrospection/TodayAppointmentObject.h @@ -80,6 +80,18 @@ concept getForceError = requires (TImpl impl) { service::AwaitableScalar> { impl.getForceError() } }; }; +template +concept getArrayWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableScalar> { impl.getArray(std::move(params)) } }; +}; + +template +concept getArray = requires (TImpl impl) +{ + { service::AwaitableScalar> { impl.getArray() } }; +}; + template concept beginSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) { @@ -103,6 +115,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveSubject(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveIsNow(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveForceError(service::ResolverParams&& params) const; + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveArray(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolve_typename(service::ResolverParams&& params) const; @@ -118,6 +131,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getSubject(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar getIsNow(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getForceError(service::FieldParams&& params) const = 0; + [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getArray(service::FieldParams&& params) const = 0; }; template @@ -209,6 +223,22 @@ class [[nodiscard("unnecessary construction")]] Appointment final } } + [[nodiscard("unnecessary call")]] service::AwaitableScalar> getArray(service::FieldParams&& params) const override + { + if constexpr (methods::AppointmentHas::getArrayWithParams) + { + return { _pimpl->getArray(std::move(params)) }; + } + else if constexpr (methods::AppointmentHas::getArray) + { + return { _pimpl->getArray() }; + } + else + { + throw service::unimplemented_method(R"ex(Appointment::getArray)ex"); + } + } + void beginSelectionSet(const service::SelectionSetParams& params) const override { if constexpr (methods::AppointmentHas::beginSelectionSet) diff --git a/samples/today/schema.today.graphql b/samples/today/schema.today.graphql index 94e9770a..59b77ae9 100644 --- a/samples/today/schema.today.graphql +++ b/samples/today/schema.today.graphql @@ -132,6 +132,7 @@ type Appointment implements Node { subject: String isNow: Boolean! forceError: String + array: [ID!]! } type Task implements Node { diff --git a/samples/today/schema/AppointmentObject.cpp b/samples/today/schema/AppointmentObject.cpp index 39f8910e..ada849cd 100644 --- a/samples/today/schema/AppointmentObject.cpp +++ b/samples/today/schema/AppointmentObject.cpp @@ -39,6 +39,7 @@ service::ResolverMap Appointment::getResolvers() const noexcept return { { R"gql(id)gql"sv, [this](service::ResolverParams&& params) { return resolveId(std::move(params)); } }, { R"gql(when)gql"sv, [this](service::ResolverParams&& params) { return resolveWhen(std::move(params)); } }, + { R"gql(array)gql"sv, [this](service::ResolverParams&& params) { return resolveArray(std::move(params)); } }, { R"gql(isNow)gql"sv, [this](service::ResolverParams&& params) { return resolveIsNow(std::move(params)); } }, { R"gql(subject)gql"sv, [this](service::ResolverParams&& params) { return resolveSubject(std::move(params)); } }, { R"gql(__typename)gql"sv, [this](service::ResolverParams&& params) { return resolve_typename(std::move(params)); } }, @@ -111,6 +112,17 @@ service::AwaitableResolver Appointment::resolveForceError(service::ResolverParam return service::ModifiedResult::convert(std::move(result), std::move(params)); } +service::AwaitableResolver Appointment::resolveArray(service::ResolverParams&& params) const +{ + std::unique_lock resolverLock(_resolverMutex); + service::SelectionSetParams selectionSetParams { static_cast(params) }; + auto directives = std::move(params.fieldDirectives); + auto result = _pimpl->getArray(service::FieldParams { std::move(selectionSetParams), std::move(directives) }); + resolverLock.unlock(); + + return service::ModifiedResult::convert(std::move(result), std::move(params)); +} + service::AwaitableResolver Appointment::resolve_typename(service::ResolverParams&& params) const { return service::Result::convert(std::string{ R"gql(Appointment)gql" }, std::move(params)); @@ -128,7 +140,8 @@ void AddAppointmentDetails(const std::shared_ptr& typeAppoin schema::Field::Make(R"gql(when)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(DateTime)gql"sv)), schema::Field::Make(R"gql(subject)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), schema::Field::Make(R"gql(isNow)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(Boolean)gql"sv))), - schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)) + schema::Field::Make(R"gql(forceError)gql"sv, R"md()md"sv, std::nullopt, schema->LookupType(R"gql(String)gql"sv)), + schema::Field::Make(R"gql(array)gql"sv, R"md()md"sv, std::nullopt, schema->WrapType(introspection::TypeKind::NON_NULL, schema->WrapType(introspection::TypeKind::LIST, schema->WrapType(introspection::TypeKind::NON_NULL, schema->LookupType(R"gql(ID)gql"sv))))) }); } diff --git a/samples/today/schema/TodayAppointmentObject.h b/samples/today/schema/TodayAppointmentObject.h index af90d938..7507a086 100644 --- a/samples/today/schema/TodayAppointmentObject.h +++ b/samples/today/schema/TodayAppointmentObject.h @@ -80,6 +80,18 @@ concept getForceError = requires (TImpl impl) { service::AwaitableScalar> { impl.getForceError() } }; }; +template +concept getArrayWithParams = requires (TImpl impl, service::FieldParams params) +{ + { service::AwaitableScalar> { impl.getArray(std::move(params)) } }; +}; + +template +concept getArray = requires (TImpl impl) +{ + { service::AwaitableScalar> { impl.getArray() } }; +}; + template concept beginSelectionSet = requires (TImpl impl, const service::SelectionSetParams params) { @@ -103,6 +115,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveSubject(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveIsNow(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveForceError(service::ResolverParams&& params) const; + [[nodiscard("unnecessary call")]] service::AwaitableResolver resolveArray(service::ResolverParams&& params) const; [[nodiscard("unnecessary call")]] service::AwaitableResolver resolve_typename(service::ResolverParams&& params) const; @@ -118,6 +131,7 @@ class [[nodiscard("unnecessary construction")]] Appointment final [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getSubject(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar getIsNow(service::FieldParams&& params) const = 0; [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getForceError(service::FieldParams&& params) const = 0; + [[nodiscard("unnecessary call")]] virtual service::AwaitableScalar> getArray(service::FieldParams&& params) const = 0; }; template @@ -209,6 +223,22 @@ class [[nodiscard("unnecessary construction")]] Appointment final } } + [[nodiscard("unnecessary call")]] service::AwaitableScalar> getArray(service::FieldParams&& params) const override + { + if constexpr (methods::AppointmentHas::getArrayWithParams) + { + return { _pimpl->getArray(std::move(params)) }; + } + else if constexpr (methods::AppointmentHas::getArray) + { + return { _pimpl->getArray() }; + } + else + { + throw service::unimplemented_method(R"ex(Appointment::getArray)ex"); + } + } + void beginSelectionSet(const service::SelectionSetParams& params) const override { if constexpr (methods::AppointmentHas::beginSelectionSet) diff --git a/src/ClientGenerator.cpp b/src/ClientGenerator.cpp index 6f3de4a9..857e892e 100644 --- a/src/ClientGenerator.cpp +++ b/src/ClientGenerator.cpp @@ -2203,6 +2203,11 @@ void Generator::outputResponseFieldVisitorReserve(std::ostream& sourceFile, } } + if (dereference) + { + accessor.append(R"cpp(.)cpp"); + } + std::unordered_set fieldNames; switch (responseField.type->kind()) @@ -2211,37 +2216,6 @@ void Generator::outputResponseFieldVisitorReserve(std::ostream& sourceFile, 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) @@ -2263,6 +2237,12 @@ void Generator::outputResponseFieldVisitorStartObject(std::ostream& sourceFile, std::string_view parentAccessor /* = {} */, std::string_view parentCppType /* = {} */) const noexcept { + if (responseField.type->kind() == introspection::TypeKind::SCALAR + && SchemaLoader::getBuiltinTypes().contains(responseField.type->name())) + { + return; + } + auto state = std::format("{}_{}", parentState.empty() ? R"cpp(Member)cpp"sv : parentState, responseField.cppName);