blob: 23f72d7d56fa12c404b2b6106e247ba19c0cb9cd [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_INTERNAL_MAKE_RESPONSE_CONTEXT_H_
#define SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_INTERNAL_MAKE_RESPONSE_CONTEXT_H_
#include <lib/fidl/cpp/any_error_in.h>
#include <lib/fidl/cpp/unified_messaging.h>
#include <lib/fidl/llcpp/client_base.h>
#include <optional>
namespace fidl {
namespace internal {
// |DecodeResponseAndFoldError| decodes an incoming message |incoming| returns
// a transport-specific result type (e.g. |fidl::Result| for Zircon channel
// transport). In doing so it combines any FIDL application error from the error
// syntax with transport errors.
//
// If a terminal error occurred which warrants unbinding, |out_maybe_unbind|
// will be populated with a reason if not nullptr.
template <typename FidlMethod>
auto DecodeResponseAndFoldError(::fidl::IncomingMessage&& incoming,
::std::optional<::fidl::UnbindInfo>* out_maybe_unbind) {
using ResultType = typename FidlMethod::Protocol::Transport::template Result<FidlMethod>;
using NaturalResponse = ::fidl::Response<FidlMethod>;
constexpr bool HasApplicationError =
::fidl::internal::NaturalMethodTypes<FidlMethod>::HasApplicationError;
constexpr bool IsAbsentBody = ::fidl::internal::NaturalMethodTypes<FidlMethod>::IsAbsentBody;
// Check error from the underlying transport.
if (!incoming.ok()) {
ResultType error = ::fitx::error(incoming.error());
if (out_maybe_unbind != nullptr) {
out_maybe_unbind->emplace(incoming.error());
}
return error;
}
::fitx::result decoded = [&] {
if constexpr (IsAbsentBody) {
return DecodeTransactionalMessage(std::move(incoming));
} else {
using Body = typename MessageTraits<NaturalResponse>::Payload;
return DecodeTransactionalMessage<Body>(std::move(incoming));
}
}();
// Check decoding error.
if (decoded.is_error()) {
ResultType error = ::fitx::error(decoded.error_value());
if (out_maybe_unbind != nullptr) {
out_maybe_unbind->emplace(decoded.error_value());
}
return error;
}
if constexpr (IsAbsentBody) {
// Absent body.
ResultType value = ::fitx::success();
return value;
} else {
NaturalResponse response =
NaturalMessageConverter<NaturalResponse>::FromDomainObject(std::move(decoded.value()));
if constexpr (HasApplicationError) {
// Fold application error.
if (response.is_error()) {
ResultType error = response.take_error();
return error;
}
ZX_DEBUG_ASSERT(response.is_ok());
if constexpr (::fidl::internal::NaturalMethodTypes<FidlMethod>::IsEmptyStructPayload) {
// Omit empty structs.
ResultType value = ::fitx::success();
return value;
} else {
ResultType value = response.take_value();
return value;
}
} else {
ResultType value = ::fitx::ok(std::move(response));
return value;
}
}
}
// |MakeResponseContext| is a helper to create an adaptor from a |ResponseContext|
// to a response/result callback. It returns a raw pointer which deletes itself
// upon the receipt of a response or an error.
template <typename FidlMethod>
ResponseContext* MakeResponseContext(uint64_t ordinal,
::fidl::ClientCallback<FidlMethod> callback) {
using ResultType = typename FidlMethod::Protocol::Transport::template Result<FidlMethod>;
using CallbackType = ::fidl::ClientCallback<FidlMethod>;
class ResponseContext final : public ::fidl::internal::ResponseContext {
public:
explicit ResponseContext(uint64_t ordinal, CallbackType&& callback)
: ::fidl::internal::ResponseContext(ordinal), callback_(std::move(callback)) {}
private:
std::optional<::fidl::UnbindInfo> OnRawResult(
::fidl::IncomingMessage&& result,
::fidl::internal::IncomingTransportContext transport_context) override {
std::optional<fidl::UnbindInfo> maybe_unbind;
ResultType value = DecodeResponseAndFoldError<FidlMethod>(std::move(result), &maybe_unbind);
callback_(value);
delete this;
return maybe_unbind;
}
::fidl::ClientCallback<FidlMethod> callback_;
};
return new ResponseContext(ordinal, std::forward<CallbackType>(callback));
}
} // namespace internal
} // namespace fidl
#endif // SRC_LIB_FIDL_CPP_INCLUDE_LIB_FIDL_CPP_INTERNAL_MAKE_RESPONSE_CONTEXT_H_