Skip to content

Commit

Permalink
Merge pull request #328 from wravery/unify-visitor
Browse files Browse the repository at this point in the history
Consolidate response::Writer with response::ValueVisitor
  • Loading branch information
wravery authored Oct 22, 2024
2 parents e85fc7e + 117120b commit abb31c3
Show file tree
Hide file tree
Showing 9 changed files with 215 additions and 206 deletions.
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

0 comments on commit abb31c3

Please sign in to comment.