#ifndef COMPONENTS_DBUS_UTILS_CALL_METHOD_H_
#define COMPONENTS_DBUS_UTILS_CALL_METHOD_H_
#include <dbus/dbus.h>
#include <numeric>
#include <string>
#include <tuple>
#include <utility>
#include "base/component_export.h"
#include "base/functional/callback.h"
#include "base/numerics/clamped_math.h"
#include "base/time/time.h"
#include "base/types/expected.h"
#include "components/dbus/utils/read_message.h"
#include "components/dbus/utils/read_value.h"
#include "components/dbus/utils/signature.h"
#include "components/dbus/utils/types.h"
#include "components/dbus/utils/variant.h"
#include "components/dbus/utils/write_value.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
namespace dbus_utils {
enum class CallMethodErrorStatus {
kErrorResponse = 0,
kExtraDataInResponse = 1,
kNoResponse = 2,
kInvalidResponseFormat = 3,
};
constexpr base::TimeDelta kTimeoutDefault =
base::Milliseconds(dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
constexpr base::TimeDelta kTimeoutMax =
base::Milliseconds(dbus::ObjectProxy::TIMEOUT_MAX);
class COMPONENT_EXPORT(COMPONENTS_DBUS) CallMethodError {
public:
CallMethodError(CallMethodErrorStatus status,
dbus::ErrorResponse* error_response);
const CallMethodErrorStatus status;
const std::string error_name;
const std::string error_message;
};
template <typename... Rets>
using CallMethodResult = base::expected<std::tuple<Rets...>, CallMethodError>;
template <SignatureLiteral RetsSignature>
using CallMethodResultSig =
base::expected<internal::ParseDBusSignaturePack<RetsSignature>,
CallMethodError>;
namespace internal {
template <typename ArgsTuple, typename Result, std::size_t... Is>
void CallMethodImpl(dbus::ObjectProxy* proxy,
const std::string& interface,
const std::string& method,
base::TimeDelta timeout,
base::OnceCallback<void(Result)> callback,
std::index_sequence<Is...>,
const std::tuple_element_t<Is, ArgsTuple>&... args) {
dbus::MethodCall dbus_call(interface, method);
dbus::MessageWriter writer(&dbus_call);
(WriteValue<std::tuple_element_t<Is, ArgsTuple>>(writer, args), ...);
base::ClampedNumeric<int32_t> timeout_ms = timeout.InMilliseconds();
proxy->CallMethodWithErrorResponse(
&dbus_call, timeout_ms,
base::BindOnce(
[](base::OnceCallback<void(Result)> cb, dbus::Response* response,
dbus::ErrorResponse* error_response) {
if (response) {
auto rets = ReadMessage<typename Result::value_type>(*response);
if (rets.has_value()) {
std::move(cb).Run(std::move(*rets));
return;
}
switch (rets.error()) {
case MessageFormatError::kInvalidMessageFormat:
std::move(cb).Run(base::unexpected(CallMethodError(
CallMethodErrorStatus::kInvalidResponseFormat, nullptr)));
return;
case MessageFormatError::kExtraDataInMessage:
std::move(cb).Run(base::unexpected(CallMethodError(
CallMethodErrorStatus::kExtraDataInResponse, nullptr)));
return;
}
} else if (error_response) {
std::move(cb).Run(base::unexpected(CallMethodError(
CallMethodErrorStatus::kErrorResponse, error_response)));
} else {
std::move(cb).Run(base::unexpected(CallMethodError(
CallMethodErrorStatus::kNoResponse, nullptr)));
}
},
std::move(callback)));
}
}
template <SignatureLiteral ArgsSignature, SignatureLiteral RetsSignature>
void CallMethod(
dbus::ObjectProxy* proxy,
const std::string& interface,
const std::string& method,
base::TimeDelta timeout,
base::OnceCallback<void(CallMethodResultSig<RetsSignature>)> callback,
const auto&... args) {
using ArgsTupleType = internal::ParseDBusSignaturePack<ArgsSignature>;
internal::CallMethodImpl<ArgsTupleType, CallMethodResultSig<RetsSignature>>(
proxy, interface, method, timeout, std::move(callback),
std::make_index_sequence<std::tuple_size_v<ArgsTupleType>>{}, args...);
}
template <SignatureLiteral ArgsSignature, SignatureLiteral RetsSignature>
void CallMethod(
dbus::ObjectProxy* proxy,
const std::string& interface,
const std::string& method,
base::OnceCallback<void(CallMethodResultSig<RetsSignature>)> callback,
const auto&... args) {
CallMethod<ArgsSignature, RetsSignature>(
proxy, interface, method, kTimeoutDefault, std::move(callback), args...);
}
}
#endif