Skip to content

Commit 4209b28

Browse files
author
rafalh
committed
Enable returning raw objects from field accessors
It allows greater isolation between resolver and objects
1 parent 9a68b07 commit 4209b28

File tree

1 file changed

+187
-1
lines changed

1 file changed

+187
-1
lines changed

include/graphqlservice/GraphQLService.h

Lines changed: 187 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,6 +410,172 @@ class [[nodiscard("unnecessary construction")]] AwaitableScalar
410410
std::variant<T, std::future<T>, std::shared_ptr<const response::Value>> _value;
411411
};
412412

413+
template <typename T>
414+
struct is_optional : std::false_type
415+
{
416+
};
417+
418+
template <typename T>
419+
struct is_optional<std::optional<T>> : std::true_type
420+
{
421+
};
422+
423+
template <typename T>
424+
struct is_vector : std::false_type
425+
{
426+
};
427+
428+
template <typename T>
429+
struct is_vector<std::vector<T>> : std::true_type
430+
{
431+
};
432+
433+
template <typename T>
434+
struct is_future : std::false_type
435+
{
436+
};
437+
438+
template <typename T>
439+
struct is_future<std::future<T>> : std::true_type
440+
{
441+
};
442+
443+
template <typename T>
444+
struct type_tag
445+
{
446+
using type = T;
447+
};
448+
449+
template <typename K, typename V>
450+
struct pair
451+
{
452+
using first_type = K;
453+
using second_type = V;
454+
};
455+
456+
template <typename Pair>
457+
struct element
458+
{
459+
static auto value(type_tag<typename Pair::first_type>) -> type_tag<typename Pair::second_type>;
460+
};
461+
462+
template <typename... elems>
463+
struct type_map : element<elems>...
464+
{
465+
using element<elems>::value...;
466+
467+
template <typename K>
468+
using find = typename decltype(type_map::value(type_tag<K> {}))::type;
469+
};
470+
471+
template <typename U, typename T>
472+
struct GraphQLUnion : std::false_type
473+
{
474+
};
475+
476+
template <typename... Types>
477+
struct Union
478+
{
479+
template <typename U>
480+
Union(U&& u)
481+
: value(std::forward<U>(u))
482+
{
483+
}
484+
std::variant<std::shared_ptr<Types>...> value;
485+
};
486+
487+
template <typename T>
488+
struct is_union : std::false_type
489+
{
490+
};
491+
492+
template <typename... Types>
493+
struct is_union<Union<Types...>> : std::true_type
494+
{
495+
};
496+
497+
// helper type for the visitor #4
498+
template <class... Ts>
499+
struct overloaded : Ts...
500+
{
501+
using Ts::operator()...;
502+
};
503+
// explicit deduction guide (not needed as of C++20)
504+
template <class... Ts>
505+
overloaded(Ts...) -> overloaded<Ts...>;
506+
507+
template <typename T>
508+
struct GraphQLBuilder
509+
{
510+
511+
template <typename U>
512+
static T build(U&& u)
513+
{
514+
515+
if constexpr (is_optional<T>::value)
516+
{
517+
if constexpr (is_optional<U>::value)
518+
{
519+
if (u)
520+
return GraphQLBuilder<typename T::value_type>::build(*u);
521+
return std::nullopt;
522+
}
523+
else
524+
{
525+
return GraphQLBuilder<typename T::value_type>::build(std::forward<U>(u));
526+
}
527+
}
528+
else if constexpr (is_vector<T>::value)
529+
{
530+
T out;
531+
for (auto ui : u)
532+
{
533+
out.push_back(GraphQLBuilder<typename T::value_type>::build(ui));
534+
}
535+
return out;
536+
}
537+
else if constexpr (is_union<typename std::remove_reference_t<U>::element_type>::value)
538+
{
539+
540+
// using model_t =
541+
// typename GraphQLUnion < typename std::remove_reference_t<U>::element_type,
542+
// typename(typename T::element_type
543+
// > ::model_map)::find<typename std::remove_reference_t<V>::element_type>;
544+
// typedef std::shared_ptr<model_t> asdf_t;
545+
546+
static_assert(GraphQLUnion<typename std::remove_reference_t<U>::element_type,
547+
typename T::element_type>::value,
548+
"template<> struct GraphQLUnion<T::element_type>: std::true_type{...} not "
549+
"defined!");
550+
if (u)
551+
return std::visit(
552+
[]<typename V>(V&& arg) {
553+
using union_t = GraphQLUnion<typename std::remove_reference_t<U>::element_type, typename T::element_type>;
554+
using model_map_t = typename union_t::model_map;
555+
using model_t = model_map_t::template find<typename std::remove_reference_t<V>::element_type>;
556+
if constexpr (std::is_same_v<model_t, std::monostate>)
557+
{
558+
throw std::logic_error("Unsupported variant type");
559+
return std::shared_ptr<typename T::element_type>();
560+
}
561+
else
562+
{
563+
return GraphQLBuilder<T>::build(
564+
GraphQLBuilder<std::shared_ptr<model_t>>::build(std::move(arg)));
565+
}
566+
},
567+
std::move(u->value));
568+
return std::shared_ptr<typename T::element_type>();
569+
}
570+
else
571+
{
572+
if (u)
573+
return std::make_shared<typename T::element_type>(std::forward<U>(u));
574+
return std::shared_ptr<typename T::element_type>();
575+
}
576+
}
577+
};
578+
413579
// Field accessors may return either a result of T, an awaitable of T, or a std::future<T>, so at
414580
// runtime the implementer may choose to return by value or defer/parallelize expensive operations
415581
// by returning an async future or an awaitable coroutine.
@@ -418,11 +584,31 @@ class [[nodiscard("unnecessary construction")]] AwaitableObject
418584
{
419585
public:
420586
template <typename U>
421-
AwaitableObject(U&& value)
587+
AwaitableObject(U&& value, std::enable_if_t<std::is_assignable_v<T, U>>* = nullptr)
422588
: _value { std::forward<U>(value) }
423589
{
424590
}
425591

592+
template <typename U>
593+
AwaitableObject(
594+
U&& value, std::enable_if_t<!std::is_assignable_v<T, U> && !is_future<U>::value>* = nullptr)
595+
: _value(GraphQLBuilder<T>::build(std::forward<U>(value)))
596+
{
597+
}
598+
599+
template <typename U>
600+
AwaitableObject(
601+
U&& value, std::enable_if_t<!std::is_assignable_v<T, U> && is_future<U>::value>* = nullptr)
602+
: _value(std::async([value = std::forward<U>(value)]() mutable {
603+
if constexpr(std::is_assignable_v<T, decltype(value.get())>){
604+
_value = value.get();
605+
}else{
606+
return GraphQLBuilder<T>::build(value.get());
607+
}
608+
}))
609+
{
610+
}
611+
426612
struct promise_type
427613
{
428614
[[nodiscard("unnecessary construction")]] AwaitableObject<T> get_return_object() noexcept

0 commit comments

Comments
 (0)