Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate response::Writer with response::ValueVisitor #328

Merged
merged 2 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/json.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ private:
virtual void end_object() const = 0;

virtual void start_array() const = 0;
virtual void end_arrary() const = 0;
virtual void end_array() const = 0;

virtual void write_null() const = 0;
virtual void write_string(const std::string& value) const = 0;
Expand Down
100 changes: 3 additions & 97 deletions include/graphqlservice/GraphQLResponse.h
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ class [[nodiscard("unnecessary construction")]] ValueVisitor final
template <class T>
ValueVisitor(std::shared_ptr<T> writer) noexcept
: _concept { std::static_pointer_cast<Concept>(
std::make_shared<Model<T>>(std::move(writer))) }
std::make_shared<Model<T>>(std::move(writer))) }
{
}

Expand Down Expand Up @@ -635,6 +635,8 @@ class [[nodiscard("unnecessary construction")]] ValueTokenStream final
ValueTokenStream() noexcept = default;
~ValueTokenStream() = default;

GRAPHQLRESPONSE_EXPORT explicit ValueTokenStream(Value&& value);

ValueTokenStream(ValueTokenStream&&) noexcept = default;
ValueTokenStream& operator=(ValueTokenStream&&) noexcept = default;

Expand Down Expand Up @@ -662,102 +664,6 @@ class [[nodiscard("unnecessary construction")]] ValueTokenStream final
std::list<ValueToken> _tokens;
};

class [[nodiscard("unnecessary construction")]] Writer final
{
private:
struct Concept
{
virtual ~Concept() = default;

virtual void start_object() const = 0;
virtual void add_member(const std::string& key) const = 0;
virtual void end_object() const = 0;

virtual void start_array() const = 0;
virtual void end_arrary() const = 0;

virtual void write_null() const = 0;
virtual void write_string(const std::string& value) const = 0;
virtual void write_bool(bool value) const = 0;
virtual void write_int(int value) const = 0;
virtual void write_float(double value) const = 0;
};

template <class T>
struct Model : Concept
{
explicit Model(std::unique_ptr<T> pimpl) noexcept
: _pimpl { std::move(pimpl) }
{
}

void start_object() const final
{
_pimpl->start_object();
}

void add_member(const std::string& key) const final
{
_pimpl->add_member(key);
}

void end_object() const final
{
_pimpl->end_object();
}

void start_array() const final
{
_pimpl->start_array();
}

void end_arrary() const final
{
_pimpl->end_arrary();
}

void write_null() const final
{
_pimpl->write_null();
}

void write_string(const std::string& value) const final
{
_pimpl->write_string(value);
}

void write_bool(bool value) const final
{
_pimpl->write_bool(value);
}

void write_int(int value) const final
{
_pimpl->write_int(value);
}

void write_float(double value) const final
{
_pimpl->write_float(value);
}

private:
std::unique_ptr<T> _pimpl;
};

const std::shared_ptr<const Concept> _concept;

public:
template <class T>
Writer(std::unique_ptr<T> writer) noexcept
: _concept { std::static_pointer_cast<const Concept>(
std::make_shared<Model<T>>(std::move(writer))) }
{
}

GRAPHQLRESPONSE_EXPORT void write(Value value) const;
};

} // namespace graphql::response

#endif // GRAPHQLRESPONSE_H
4 changes: 4 additions & 0 deletions include/graphqlservice/GraphQLService.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ struct [[nodiscard("unnecessary construction")]] schema_error
[[nodiscard("unnecessary memory copy")]] GRAPHQLSERVICE_EXPORT response::Value buildErrorValues(
std::list<schema_error>&& structuredErrors);

[[nodiscard("unnecessary memory copy")]] GRAPHQLSERVICE_EXPORT response::ValueTokenStream
visitErrorValues(std::list<schema_error>&& structuredErrors);

// This exception bubbles up 1 or more error messages to the JSON results.
class [[nodiscard("unnecessary construction")]] schema_exception : public std::exception
{
Expand Down Expand Up @@ -540,6 +543,7 @@ struct [[nodiscard("unnecessary construction")]] ResolverParams : SelectionSetPa
struct [[nodiscard("unnecessary construction")]] ResolverResult
{
[[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT response::Value document() &&;
[[nodiscard("unnecessary call")]] GRAPHQLSERVICE_EXPORT response::ValueTokenStream visit() &&;

response::ValueTokenStream data {};
std::list<schema_error> errors {};
Expand Down
2 changes: 0 additions & 2 deletions include/graphqlservice/Response.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ using response::AwaitableValue;
using response::ValueVisitor;
using response::ValueToken;
using response::ValueTokenStream;

using response::Writer;
// clang-format on

} // namespace graphql::response
1 change: 1 addition & 0 deletions include/graphqlservice/Service.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ using service::buildErrorPath;

using service::schema_error;
using service::buildErrorValues;
using service::visitErrorValues;

using service::schema_exception;
using service::unimplemented_method;
Expand Down
112 changes: 64 additions & 48 deletions src/GraphQLResponse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ bool IdType::operator==(const IdType& rhs) const noexcept

return (std::holds_alternative<ByteData>(_data)
? internal::Base64::compareBase64(std::get<ByteData>(_data),
std::get<OpaqueString>(rhs._data))
std::get<OpaqueString>(rhs._data))
: internal::Base64::compareBase64(std::get<ByteData>(rhs._data),
std::get<OpaqueString>(_data)))
std::get<OpaqueString>(_data)))
== internal::Base64::Comparison::EqualTo;
}

Expand Down Expand Up @@ -146,7 +146,7 @@ bool IdType::operator<(const IdType& rhs) const noexcept
return (std::holds_alternative<ByteData>(_data)
? (internal::Base64::compareBase64(std::get<ByteData>(_data),
std::get<OpaqueString>(rhs._data))
< internal::Base64::Comparison::EqualTo)
< internal::Base64::Comparison::EqualTo)
: (internal::Base64::compareBase64(std::get<ByteData>(rhs._data),
std::get<OpaqueString>(_data)))
> internal::Base64::Comparison::EqualTo);
Expand Down Expand Up @@ -1802,111 +1802,127 @@ void ValueTokenStreamVisitor::add_value(Value&& value)
}
}

void ValueTokenStream::append(ValueTokenStream&& other)
{
_tokens.splice(_tokens.end(), std::move(other._tokens));
}

void ValueTokenStream::visit(const std::shared_ptr<ValueVisitor>& visitor) &&
ValueTokenStream::ValueTokenStream(Value&& value)
{
for (auto& token : _tokens)
{
std::move(token).visit(visitor);
}

visitor->complete();
}

Value ValueTokenStream::value() &&
{
auto visitor = std::make_shared<ValueTokenStreamVisitor>();

std::move(*this).visit(std::make_shared<ValueVisitor>(visitor));

return visitor->value();
}

void Writer::write(Value response) const
{
switch (response.type())
switch (value.type())
{
case Type::Map:
{
auto members = response.release<MapType>();
auto members = value.release<MapType>();

_concept->start_object();
push_back(ValueToken::StartObject {});
push_back(ValueToken::Reserve { members.size() });

for (auto& entry : members)
{
_concept->add_member(entry.first);
write(std::move(entry.second));
push_back(ValueToken::AddMember { std::move(entry.first) });
append(ValueTokenStream { std::move(entry.second) });
}

_concept->end_object();
push_back(ValueToken::EndObject {});
break;
}

case Type::List:
{
auto elements = response.release<ListType>();
auto elements = value.release<ListType>();

_concept->start_array();
push_back(ValueToken::StartArray {});
push_back(ValueToken::Reserve { elements.size() });

for (auto& entry : elements)
{
write(std::move(entry));
append(ValueTokenStream { std::move(entry) });
}

_concept->end_arrary();
push_back(ValueToken::EndArray {});
break;
}

case Type::String:
case Type::EnumValue:
case Type::ID:
{
auto value = response.release<StringType>();
auto stringValue = value.release<StringType>();

_concept->write_string(value);
push_back(ValueToken::StringValue { std::move(stringValue) });
break;
}

case Type::Null:
{
_concept->write_null();
push_back(ValueToken::NullValue {});
break;
}

case Type::Boolean:
{
_concept->write_bool(response.get<BooleanType>());
push_back(ValueToken::BoolValue { value.get<BooleanType>() });
break;
}

case Type::Int:
{
_concept->write_int(response.get<IntType>());
push_back(ValueToken::IntValue { value.get<IntType>() });
break;
}

case Type::Float:
{
_concept->write_float(response.get<FloatType>());
push_back(ValueToken::FloatValue { value.get<FloatType>() });
break;
}

case Type::EnumValue:
{
auto enumValue = value.release<StringType>();

push_back(ValueToken::EnumValue { std::move(enumValue) });
break;
}

case Type::ID:
{
auto idValue = value.release<IdType>();

push_back(ValueToken::IdValue { std::move(idValue) });
break;
}

case Type::Scalar:
{
write(response.release<ScalarType>());
append(ValueTokenStream { value.release<ScalarType>() });
break;
}

default:
{
_concept->write_null();
push_back(ValueToken::NullValue {});
break;
}
}
}

void ValueTokenStream::append(ValueTokenStream&& other)
{
_tokens.splice(_tokens.end(), std::move(other._tokens));
}

void ValueTokenStream::visit(const std::shared_ptr<ValueVisitor>& visitor) &&
{
for (auto& token : _tokens)
{
std::move(token).visit(visitor);
}

visitor->complete();
}

Value ValueTokenStream::value() &&
{
auto visitor = std::make_shared<ValueTokenStreamVisitor>();

std::move(*this).visit(std::make_shared<ValueVisitor>(visitor));

return visitor->value();
}

} // namespace graphql::response
Loading