1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#pragma once
///@file RPC helper functions for `types.hh`

#include "error.hh"
#include "lix/libutil/config.hh"
#include "lix/libutil/result.hh"
#include "lix/libutil/types.capnp.h"
#include "rpc.hh"
#include <capnp/any.h>
#include <capnp/common.h>
#include <cstdint>
#include <exception>
#include <ranges>
#include <type_traits>

namespace nix::rpc {

// ensure that verbosity levels match to make conversions trivial below.
static_assert(int(Verbosity::ERROR) == int(nix::Verbosity::lvlError));
static_assert(int(Verbosity::WARN) == int(nix::Verbosity::lvlWarn));
static_assert(int(Verbosity::NOTICE) == int(nix::Verbosity::lvlNotice));
static_assert(int(Verbosity::INFO) == int(nix::Verbosity::lvlInfo));
static_assert(int(Verbosity::TALKATIVE) == int(nix::Verbosity::lvlTalkative));
static_assert(int(Verbosity::CHATTY) == int(nix::Verbosity::lvlChatty));
static_assert(int(Verbosity::DEBUG) == int(nix::Verbosity::lvlDebug));
static_assert(int(Verbosity::VOMIT) == int(nix::Verbosity::lvlVomit));

inline nix::ErrorInfo from(const Error::Reader & e, auto &&... args)
{
    ErrorInfo ei{
        // capnp enums are u16, and we have checked that the enumerator values match
        .level = nix::Verbosity(std::clamp<std::underlying_type_t<nix::Verbosity>>(
            uint16_t(e.getLevel()), lvlError, lvlVomit
        )),
        .msg = HintFmt(to<std::string>(e.getMessage())),
    };
    for (auto && t : e.getTraces()) {
        ei.traces.push_back(Trace{.hint = HintFmt(to<std::string>(t, args...))});
    }
    return ei;
}

template<>
struct Fill<Error, nix::ErrorInfo>
{
    static void fill(Error::Builder eb, const nix::ErrorInfo & e, auto &&...)
    {
        eb.setLevel(Verbosity(e.level));
        LIX_RPC_FILL(eb, setMessage, e.msg.str());
        LIX_RPC_FILL(eb, initTraces, e.traces | std::ranges::views::transform([](auto & trace) {
                                         return trace.hint.str();
                                     }));
    }
};

namespace detail {
inline void makeBadResult(auto rb, const std::exception_ptr & e)
{
    try {
        std::rethrow_exception(e);
    } catch (nix::Error & e) {
        LIX_RPC_FILL(rb, initBad, e.info());
    } catch (std::exception & e) { // NOLINT(lix-foreign-exceptions)
        LIX_RPC_FILL(rb, initBad, nix::Error("caught non-lix exception: %s", e.what()).info());
    } catch (...) {
        LIX_RPC_FILL(rb, initBad, nix::Error("caught non-exception! spooky").info());
    }
}

template<typename>
inline constexpr bool IsResult = false;
template<typename T>
inline constexpr bool IsResult<Result<T>> = true;

template<typename T>
concept ResultReader = IsResult<typename T::Reads>;

template<typename T>
concept ResultBuilder = IsResult<typename T::Builds>;
}

inline nix::Result<void> from(const ResultV::Reader & r, auto &&... args)
{
    if (r.isGood()) {
        return result::success();
    } else {
        return result::failure(nix::Error(from(r.getBad(), args...)));
    }
}

template<>
struct Fill<ResultV, nix::Result<void>>
{
    static void fill(ResultV::Builder rb, const nix::Result<void> & r, auto &&...)
    {
        if (r.has_value()) {
            rb.setGood();
        } else {
            detail::makeBadResult(rb, r.error());
        }
    }
};

template<>
struct Fill<ResultV, std::exception_ptr>
{
    static void fill(ResultV::Builder rb, const std::exception_ptr & e, auto &&...)
    {
        detail::makeBadResult(rb, e);
    }
};

inline auto from(detail::ResultReader auto r, auto &&... args)
{
    using R = nix::Result<decltype(r.getGood())>;
    if (r.isGood()) {
        return R(result::success(r.getGood()));
    } else {
        return R(result::failure(nix::Error(from(r.getBad(), args...))));
    }
}

template<typename T>
struct Fill<Result<T>, std::exception_ptr>
{
    static void fill(Result<T>::Builder rb, const std::exception_ptr & e, auto &&...)
    {
        detail::makeBadResult(rb, e);
    }
};

template<>
struct Fill<Settings::Setting, std::pair<const std::string, Config::SettingInfo>>
{
    static void fill(
        Settings::Setting::Builder sb,
        const std::pair<const std::string, Config::SettingInfo> & s,
        auto &&... args
    )
    {
        LIX_RPC_FILL(sb, setName, s.first);
        LIX_RPC_FILL(sb, setValue, s.second.value);
    }
};

template<>
struct Fill<Settings, std::map<std::string, Config::SettingInfo>>
{
    static void fill(
        Settings::Builder sb, const std::map<std::string, Config::SettingInfo> & s, auto &&... args
    )
    {
        LIX_RPC_FILL(sb, initMap, s);
    }
};

template<>
struct Convert<Settings, std::map<std::string, std::string>>
{
    static std::map<std::string, std::string> convert(const Settings::Reader & t, auto &&...)
    {
        std::map<std::string, std::string> result;
        for (const auto & s : t.getMap()) {
            result[rpc::to<std::string>(s.getName())] = rpc::to<std::string>(s.getValue());
        }
        return result;
    }
};
}