libutil/rpc-types: Add Option type
Co-authored-by: piegames <git@piegames.de> Change-Id: I75d5db5f567a3a86870ed2aceb36840ddcbb53db
This commit is contained in:
co-authored by
piegames
parent
66090810f1
commit
7307475218
@@ -8,12 +8,15 @@
|
||||
#include "rpc.hh"
|
||||
#include <capnp/any.h>
|
||||
#include <capnp/common.h>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <ranges>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace nix::rpc {
|
||||
|
||||
@@ -93,6 +96,62 @@ struct Convert<Settings, std::map<std::string, std::string>>
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template<typename>
|
||||
struct OptionArgT;
|
||||
template<typename T>
|
||||
struct OptionArgT<Option<T>>
|
||||
{
|
||||
using type = T;
|
||||
};
|
||||
template<>
|
||||
struct OptionArgT<OptionInt64>
|
||||
{
|
||||
using type = int64_t;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using OptionArg = typename OptionArgT<T>::type;
|
||||
|
||||
template<typename T>
|
||||
concept OptionT = requires { OptionArgT<T>{}; };
|
||||
template<typename T>
|
||||
concept OptionReaderT = OptionT<typename T::Reads>;
|
||||
}
|
||||
|
||||
template<detail::OptionT From, typename To>
|
||||
struct Convert<From, std::optional<To>>
|
||||
{
|
||||
template<typename... Args>
|
||||
requires requires(From::Reader r, Args... args) { to<To>(r.getSome(), args...); }
|
||||
static std::optional<To> convert(const typename From::Reader & r, Args &&... args)
|
||||
{
|
||||
return r.isSome() ? std::optional<To>{std::in_place, to<To>(r.getSome(), args...)} : std::nullopt;
|
||||
}
|
||||
};
|
||||
|
||||
template<detail::OptionT O, typename T>
|
||||
struct Fill<O, std::optional<T>>
|
||||
{
|
||||
template<typename... Args>
|
||||
static void fill(O::Builder builder, const std::optional<T> & from, Args &&... args)
|
||||
{
|
||||
if (from.has_value()) {
|
||||
LIX_RPC_FILL_GENERIC_DEPENDENT(builder, Some, from.value(), args...);
|
||||
} else {
|
||||
builder.setNone();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<detail::OptionReaderT O, typename... Args>
|
||||
requires requires(O o, Args... args) { from(o.getSome(), args...); }
|
||||
inline auto from(const O & r, Args &&... args)
|
||||
{
|
||||
using To = decltype(from(r.getSome(), args...));
|
||||
return r.isSome() ? std::optional<To>{std::in_place, from(r.getSome(), args...)} : std::nullopt;
|
||||
}
|
||||
|
||||
namespace error::v1 {
|
||||
std::string encodeLossy(const ::nix::ErrorInfo & e);
|
||||
std::optional<::nix::ErrorInfo> tryDecode(std::string_view source);
|
||||
|
||||
@@ -46,6 +46,21 @@ struct Error {
|
||||
traces @2 :List(Data);
|
||||
}
|
||||
|
||||
struct Option(T) {
|
||||
union {
|
||||
# make sure uninitialized options deserialize to none for some added safety
|
||||
none @0 :Void;
|
||||
some @1 :T;
|
||||
}
|
||||
}
|
||||
|
||||
struct OptionInt64 {
|
||||
union {
|
||||
none @0 :Void;
|
||||
some @1 :Int64;
|
||||
}
|
||||
}
|
||||
|
||||
struct Settings {
|
||||
struct Setting {
|
||||
name @0 :Data;
|
||||
|
||||
@@ -1,9 +1,58 @@
|
||||
#include "lix/libutil/rpc.hh"
|
||||
#include "lix/libutil/error.hh"
|
||||
#include "lix/libutil/fmt.hh"
|
||||
#include "lix/libutil/types-rpc.hh"
|
||||
#include "types.capnp.h"
|
||||
#include <capnp/blob.h>
|
||||
#include <capnp/list.h>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <gtest/gtest.h>
|
||||
#include <optional>
|
||||
|
||||
namespace nix {
|
||||
TEST(RpcConverters, DISABLED_optionConvertersCompile)
|
||||
{
|
||||
using namespace rpc;
|
||||
|
||||
// primitive inner
|
||||
RPC_FILL_STRUCT(Option<OptionInt64>::Builder{nullptr}, initSome, std::optional<int64_t>{});
|
||||
static_assert(std::same_as<decltype(from(OptionInt64::Reader{})), std::optional<int64_t>>);
|
||||
|
||||
// string inner, which is special (of course)
|
||||
RPC_FILL_STRUCT(Option<Option<capnp::Text>>::Builder{nullptr}, initSome, std::optional<std::string>{});
|
||||
static_assert(std::same_as<
|
||||
decltype(to<std::optional<std::string>>(Option<capnp::Text>::Reader{})),
|
||||
std::optional<std::string>>);
|
||||
static_assert(std::same_as<
|
||||
decltype(to<std::optional<std::string_view>>(Option<capnp::Text>::Reader{})),
|
||||
std::optional<std::string_view>>);
|
||||
|
||||
// struct inner
|
||||
RPC_FILL_STRUCT(Option<Option<rpc::Error>>::Builder{nullptr}, initSome, std::optional<ErrorInfo>{});
|
||||
static_assert(std::same_as<decltype(from(Option<rpc::Error>::Reader{})), std::optional<ErrorInfo>>);
|
||||
|
||||
// list inner
|
||||
RPC_FILL_STRUCT(
|
||||
Option<Option<capnp::List<capnp::Text>>>::Builder{nullptr},
|
||||
initSome,
|
||||
std::optional<std::list<std::string>>{}
|
||||
);
|
||||
static_assert(
|
||||
std::same_as<
|
||||
decltype(to<std::optional<std::list<std::string>>>(Option<capnp::List<capnp::Text>>::Reader{})),
|
||||
std::optional<std::list<std::string>>>
|
||||
);
|
||||
|
||||
// nested inner
|
||||
RPC_FILL_STRUCT(
|
||||
Option<Option<OptionInt64>>::Builder{nullptr}, initSome, std::optional<std::optional<int64_t>>{}
|
||||
);
|
||||
static_assert(
|
||||
std::same_as<decltype(from(Option<OptionInt64>::Reader{})), std::optional<std::optional<int64_t>>>
|
||||
);
|
||||
}
|
||||
|
||||
TEST(RpcErrorV1, shortMessage)
|
||||
{
|
||||
Error e{ErrorInfo{lvlWarn, HintFmt("test message %s", "data")}};
|
||||
|
||||
Reference in New Issue
Block a user