14#include <fmt/format.h> 
   16#include <userver/compiler/demangle.hpp> 
   17#include <userver/utils/assert.hpp> 
   19USERVER_NAMESPACE_BEGIN
 
   25constexpr bool HasUppercaseAscii(std::string_view value) 
noexcept {
 
   26  for (
auto c : value) {
 
   27    if (
'A' <= c && c <= 
'Z') 
return true;
 
   33constexpr bool ICaseEqualLowercase(std::string_view lowercase,
 
   34                                   std::string_view y) 
noexcept {
 
   35  const auto size = lowercase.size();
 
   37  constexpr char kLowerToUpperMask = 
static_cast<
char>(~
unsigned{32});
 
   38  for (std::size_t i = 0; i < size; ++i) {
 
   39    const auto lowercase_c = lowercase[i];
 
   40    UASSERT(!(
'A' <= lowercase_c && lowercase_c <= 
'Z'));
 
   41    if (lowercase_c != y[i]) {
 
   42      if (!(
'a' <= lowercase_c && lowercase_c <= 
'z') ||
 
   43          (lowercase_c & kLowerToUpperMask) != y[i]) {
 
   53  constexpr explicit Found(std::size_t value) 
noexcept { 
UASSERT(value == 0); }
 
   55  constexpr explicit operator std::size_t() 
const noexcept { 
return 0; }
 
   58template <
typename Key, 
typename Value, 
typename Enabled = 
void>
 
   59class SearchState 
final {
 
   61  constexpr explicit SearchState(Key key) 
noexcept 
   62      : key_or_result_(std::in_place_index<0>, key) {}
 
   64  constexpr bool IsFound() 
const noexcept {
 
   65    return key_or_result_.index() != 0;
 
   68  constexpr Key GetKey() 
const noexcept {
 
   70    return std::get<0>(key_or_result_);
 
   73  constexpr void SetValue(Value value) 
noexcept {
 
   74    key_or_result_ = std::variant<Key, Value>(std::in_place_index<1>, value);
 
   77  [[nodiscard]] 
constexpr std::optional<Value> Extract() 
noexcept {
 
   78    if (key_or_result_.index() == 1) {
 
   79      return std::get<1>(key_or_result_);
 
   86  std::variant<Key, Value> key_or_result_;
 
   89inline constexpr std::size_t kInvalidSize =
 
   90    std::numeric_limits<std::size_t>::max();
 
   92template <
typename Payload>
 
   93inline constexpr bool kFitsInStringOrPayload =
 
   94    sizeof(Payload) <= 
sizeof(
const char*) &&
 
   95    (std::is_integral_v<Payload> || std::is_enum_v<Payload> ||
 
   96     std::is_same_v<Payload, Found>);
 
   99template <
typename Payload>
 
  100class StringOrPayload 
final {
 
  102  constexpr explicit StringOrPayload(std::string_view string) 
noexcept 
  103      : data_or_payload_(string.data()), size_(string.size()) {
 
  104#if defined(__clang__
) 
  105    __builtin_assume(size_ != kInvalidSize);
 
  106#elif defined(__GNUC__) 
  107    if (size_ == kInvalidSize) __builtin_unreachable();
 
  111  constexpr explicit StringOrPayload(Payload payload) 
noexcept 
  112      : data_or_payload_(payload), size_(kInvalidSize) {}
 
  114  constexpr bool HasPayload() 
const noexcept { 
return size_ == kInvalidSize; }
 
  116  constexpr std::string_view GetString() 
const noexcept {
 
  118    return std::string_view{data_or_payload_.data, size_};
 
  121  constexpr Payload GetPayload() 
const noexcept {
 
  123    return data_or_payload_.payload;
 
  127  static_assert(kFitsInStringOrPayload<Payload>);
 
  129  union DataOrPayload {
 
  130    constexpr explicit DataOrPayload(
const char* data) 
noexcept : data(data) {}
 
  132    constexpr explicit DataOrPayload(Payload payload) 
noexcept 
  133        : payload(payload) {}
 
  139  DataOrPayload data_or_payload_;
 
  143template <
typename Value>
 
  144class SearchState<std::string_view, Value,
 
  145                  std::enable_if_t<kFitsInStringOrPayload<Value>>>
 
  148  constexpr explicit SearchState(std::string_view key) 
noexcept : state_(key) {}
 
  150  constexpr bool IsFound() 
const noexcept { 
return state_.HasPayload(); }
 
  152  constexpr std::string_view GetKey() 
const noexcept {
 
  153    return state_.GetString();
 
  156  constexpr void SetValue(Value value) 
noexcept {
 
  157    state_ = StringOrPayload<Value>{value};
 
  160  [[nodiscard]] 
constexpr std::optional<Value> Extract() 
noexcept {
 
  161    return IsFound() ? std::optional{state_.GetPayload()} : std::nullopt;
 
  165  StringOrPayload<Value> state_;
 
  168template <
typename Key>
 
  169class SearchState<Key, std::string_view,
 
  170                  std::enable_if_t<kFitsInStringOrPayload<Key>>>
 
  173  constexpr explicit SearchState(Key key) 
noexcept : state_(key) {}
 
  175  constexpr bool IsFound() 
const noexcept { 
return !state_.HasPayload(); }
 
  177  constexpr Key GetKey() 
const noexcept { 
return state_.GetPayload(); }
 
  179  constexpr void SetValue(std::string_view value) 
noexcept {
 
  180    state_ = StringOrPayload<Key>{value};
 
  183  [[nodiscard]] 
constexpr std::optional<std::string_view> Extract() 
noexcept {
 
  184    return IsFound() ? std::optional{state_.GetString()} : std::nullopt;
 
  188  StringOrPayload<Key> state_;
 
  191template <
typename First, 
typename Second>
 
  192class SwitchByFirst 
final {
 
  194  constexpr explicit SwitchByFirst(First search) 
noexcept : state_(search) {}
 
  196  constexpr SwitchByFirst& Case(First first, Second second) 
noexcept {
 
  197    if (!state_.IsFound() && state_.GetKey() == first) {
 
  198      state_.SetValue(second);
 
  203  template <
typename T, 
typename U = 
void>
 
  204  constexpr SwitchByFirst& Type() {
 
  208  [[nodiscard]] 
constexpr std::optional<Second> Extract() 
noexcept {
 
  209    return state_.Extract();
 
  213  SearchState<First, Second> state_;
 
  216template <
typename First>
 
  217class SwitchByFirst<First, 
void> 
final {
 
  219  constexpr explicit SwitchByFirst(First search) 
noexcept : state_(search) {}
 
  221  constexpr SwitchByFirst& Case(First first) 
noexcept {
 
  222    if (!state_.IsFound() && state_.GetKey() == first) {
 
  223      state_.SetValue(Found{0});
 
  228  template <
typename T, 
typename U = 
void>
 
  229  constexpr SwitchByFirst& Type() {
 
  233  [[nodiscard]] 
constexpr bool Extract() 
noexcept { 
return state_.IsFound(); }
 
  236  SearchState<First, Found> state_;
 
  239template <
typename Second>
 
  240class SwitchByFirstICase 
final {
 
  242  constexpr explicit SwitchByFirstICase(std::string_view search) 
noexcept 
  245  constexpr SwitchByFirstICase& Case(std::string_view first,
 
  246                                     Second second) 
noexcept {
 
  248                fmt::format(
"String literal '{}' in utils::Switch*::Case() " 
  249                            "should be in lower case",
 
  251    if (!state_.IsFound() && state_.GetKey().size() == first.size() &&
 
  252        impl::ICaseEqualLowercase(first, state_.GetKey())) {
 
  253      state_.SetValue(second);
 
  258  template <
typename T, 
typename U>
 
  259  constexpr SwitchByFirstICase& Type() {
 
  263  [[nodiscard]] 
constexpr std::optional<Second> Extract() 
noexcept {
 
  264    return state_.Extract();
 
  268  SearchState<std::string_view, Second> state_;
 
  272class SwitchByFirstICase<
void> 
final {
 
  274  constexpr explicit SwitchByFirstICase(std::string_view search) 
noexcept 
  277  constexpr SwitchByFirstICase& Case(std::string_view first) 
noexcept {
 
  279                fmt::format(
"String literal '{}' in utils::Switch*::Case() " 
  280                            "should be in lower case",
 
  282    if (!state_.IsFound() && state_.GetKey().size() == first.size() &&
 
  283        impl::ICaseEqualLowercase(first, state_.GetKey())) {
 
  284      state_.SetValue(Found{0});
 
  289  template <
typename T, 
typename U>
 
  290  constexpr SwitchByFirstICase& Type() {
 
  294  [[nodiscard]] 
constexpr bool Extract() 
const noexcept {
 
  295    return state_.IsFound();
 
  299  SearchState<std::string_view, Found> state_;
 
  302template <
typename First>
 
  303class SwitchBySecondICase 
final {
 
  305  constexpr explicit SwitchBySecondICase(std::string_view search) 
noexcept 
  308  constexpr SwitchBySecondICase& Case(First first,
 
  309                                      std::string_view second) 
noexcept {
 
  311                fmt::format(
"String literal '{}' in utils::Switch*::Case() " 
  312                            "should be in lower case",
 
  314    if (!state_.IsFound() && state_.GetKey().size() == second.size() &&
 
  315        impl::ICaseEqualLowercase(second, state_.GetKey())) {
 
  316      state_.SetValue(first);
 
  321  template <
typename T, 
typename U>
 
  322  constexpr SwitchBySecondICase& Type() {
 
  326  [[nodiscard]] 
constexpr std::optional<First> Extract() 
noexcept {
 
  327    return state_.Extract();
 
  331  SearchState<std::string_view, First> state_;
 
  334template <
typename First, 
typename Second>
 
  335class SwitchBySecond 
final {
 
  337  constexpr explicit SwitchBySecond(Second search) 
noexcept : state_(search) {}
 
  339  constexpr SwitchBySecond& Case(First first, Second second) 
noexcept {
 
  340    if (!state_.IsFound() && state_.GetKey() == second) {
 
  341      state_.SetValue(first);
 
  346  template <
typename T, 
typename U>
 
  347  constexpr SwitchBySecond& Type() {
 
  351  [[nodiscard]] 
constexpr std::optional<First> Extract() 
noexcept {
 
  352    return state_.Extract();
 
  356  SearchState<Second, First> state_;
 
  359template <
typename First, 
typename Second>
 
  360class SwitchTypesDetected 
final {
 
  362  using first_type = First;
 
  363  using second_type = Second;
 
  365  constexpr SwitchTypesDetected& Case(First, Second) 
noexcept { 
return *
this; }
 
  368template <
typename First>
 
  369class SwitchTypesDetected<First, 
void> 
final {
 
  371  using first_type = First;
 
  372  using second_type = 
void;
 
  374  constexpr SwitchTypesDetected& Case(First) 
noexcept { 
return *
this; }
 
  377class SwitchTypesDetector 
final {
 
  379  constexpr SwitchTypesDetector& operator()() 
noexcept { 
return *
this; }
 
  381  template <
typename First, 
typename Second>
 
  382  constexpr auto Case(First, Second) 
noexcept {
 
  383    return Type<First, Second>();
 
  386  template <
typename First>
 
  387  constexpr auto Case(First) 
noexcept {
 
  388    return Type<First, 
void>();
 
  391  template <
typename First, 
typename Second = 
void>
 
  392  constexpr auto Type() 
noexcept {
 
  394        std::conditional_t<std::is_convertible_v<First, std::string_view>,
 
  395                           std::string_view, First>;
 
  397        std::conditional_t<std::is_convertible_v<Second, std::string_view>,
 
  398                           std::string_view, Second>;
 
  399    return SwitchTypesDetected<first_type, second_type>{};
 
  403class CaseCounter 
final {
 
  405  template <
typename First, 
typename Second>
 
  406  constexpr CaseCounter& Case(First, Second) 
noexcept {
 
  411  template <
typename First>
 
  412  constexpr CaseCounter& Case(First) 
noexcept {
 
  417  template <
typename T, 
typename U>
 
  418  constexpr CaseCounter& Type() {
 
  422  [[nodiscard]] 
constexpr std::size_t Extract() 
const noexcept {
 
  427  std::size_t count_{0};
 
  430class CaseDescriber 
final {
 
  432  template <
typename First, 
typename Second>
 
  433  CaseDescriber& Case(First first, Second second) 
noexcept {
 
  434    if (!description_.empty()) {
 
  435      description_ += 
", ";
 
  438    description_ += fmt::format(
"('{}', '{}')", first, second);
 
  443  template <
typename T, 
typename U>
 
  444  constexpr CaseDescriber& Type() {
 
  448  [[nodiscard]] std::string Extract() && 
noexcept {
 
  449    return std::move(description_);
 
  453  std::string description_{};
 
  456class CaseFirstDescriber 
final {
 
  458  template <
typename First>
 
  459  CaseFirstDescriber& Case(First first) 
noexcept {
 
  460    if (!description_.empty()) {
 
  461      description_ += 
", ";
 
  464    description_ += fmt::format(
"'{}'", first);
 
  469  template <
typename First, 
typename Second>
 
  470  CaseFirstDescriber& Case(First first, Second ) 
noexcept {
 
  474  template <
typename T, 
typename U>
 
  475  constexpr CaseFirstDescriber& Type() {
 
  479  [[nodiscard]] std::string Extract() && 
noexcept {
 
  480    return std::move(description_);
 
  484  std::string description_{};
 
  487class CaseSecondDescriber 
final {
 
  489  template <
typename First, 
typename Second>
 
  490  CaseSecondDescriber& Case(First , Second second) 
noexcept {
 
  491    if (!description_.empty()) {
 
  492      description_ += 
", ";
 
  495    description_ += fmt::format(
"'{}'", second);
 
  500  template <
typename T, 
typename U>
 
  501  constexpr CaseSecondDescriber& Type() {
 
  505  [[nodiscard]] std::string Extract() && 
noexcept {
 
  506    return std::move(description_);
 
  510  std::string description_{};
 
  513template <
typename First, 
typename Second>
 
  514class CaseGetValuesByIndex 
final {
 
  516  explicit constexpr CaseGetValuesByIndex(std::size_t search_index)
 
  517      : index_(search_index + 1) {}
 
  519  constexpr CaseGetValuesByIndex& Case(First first, Second second) 
noexcept {
 
  532  template <
typename T, 
typename U>
 
  533  constexpr CaseGetValuesByIndex& Type() {
 
  537  [[nodiscard]] 
constexpr First GetFirst() 
noexcept {
 
  538    return std::move(first_);
 
  541  [[nodiscard]] 
constexpr Second GetSecond() 
noexcept {
 
  542    return std::move(second_);
 
  551template <
typename First>
 
  552class CaseFirstIndexer 
final {
 
  554  constexpr explicit CaseFirstIndexer(First search_value) 
noexcept 
  555      : state_(search_value) {}
 
  557  constexpr CaseFirstIndexer& Case(First first) 
noexcept {
 
  558    if (!state_.IsFound() && state_.GetKey() == first) {
 
  559      state_.SetValue(index_);
 
  565  template <
typename T, 
typename U = 
void>
 
  566  constexpr CaseFirstIndexer& Type() {
 
  570  [[nodiscard]] 
constexpr std::optional<std::size_t> Extract() && 
noexcept {
 
  571    return state_.Extract();
 
  575  SearchState<First, std::size_t> state_;
 
  576  std::size_t index_ = 0;
 
  608template <
typename BuilderFunc>
 
  609class TrivialBiMap 
final {
 
  611      std::invoke_result_t<
const BuilderFunc&, impl::SwitchTypesDetector>;
 
  614  using First = 
typename TypesPair::first_type;
 
  615  using Second = 
typename TypesPair::second_type;
 
  625  using MappedTypeFor =
 
  626      std::conditional_t<std::is_convertible_v<T, First>, Second, First>;
 
  628  constexpr TrivialBiMap(BuilderFunc&& func) 
noexcept : func_(std::move(func)) {
 
  629    static_assert(std::is_empty_v<BuilderFunc>,
 
  630                  "Mapping function should not capture variables");
 
  631    static_assert(std::is_trivially_copyable_v<First>,
 
  632                  "First type in Case must be trivially copyable");
 
  633    static_assert(!std::is_void_v<Second>,
 
  634                  "If second type in Case is missing, use " 
  635                  "utils::TrivialSet instead of utils::TrivialBiMap");
 
  636    static_assert(std::is_trivially_copyable_v<Second>,
 
  637                  "Second type in Case must be trivially copyable");
 
  640  constexpr std::optional<Second> TryFindByFirst(First value) 
const noexcept {
 
  642               [value]() { 
return impl::SwitchByFirst<First, Second>{value}; })
 
  646  constexpr std::optional<First> TryFindBySecond(Second value) 
const noexcept {
 
  648               [value]() { 
return impl::SwitchBySecond<First, Second>{value}; })
 
  653  constexpr std::optional<MappedTypeFor<T>> TryFind(T value) 
const noexcept {
 
  655        !std::is_convertible_v<T, First> || !std::is_convertible_v<T, Second>,
 
  656        "Ambiguous conversion, use TryFindByFirst/TryFindBySecond instead");
 
  658    if constexpr (std::is_convertible_v<T, First>) {
 
  659      return TryFindByFirst(value);
 
  661      return TryFindBySecond(value);
 
  671    return func_([value]() { 
return impl::SwitchByFirstICase<Second>{value}; })
 
  681    return func_([value]() { 
return impl::SwitchBySecondICase<First>{value}; })
 
  689    static_assert(!std::is_convertible_v<std::string_view, First> ||
 
  690                      !std::is_convertible_v<std::string_view, Second>,
 
  691                  "Ambiguous conversion, use " 
  692                  "TryFindICaseByFirst/TryFindICaseBySecond");
 
  694    if constexpr (std::is_convertible_v<std::string_view, First>) {
 
  695      return TryFindICaseByFirst(value);
 
  697      return TryFindICaseBySecond(value);
 
  702  constexpr std::size_t 
size() 
const noexcept {
 
  703    return func_([]() { 
return impl::CaseCounter{}; }).Extract();
 
  712    return func_([]() { 
return impl::CaseDescriber{}; }).Extract();
 
  722    return func_([]() { 
return impl::CaseFirstDescriber{}; }).Extract();
 
  732    return func_([]() { 
return impl::CaseSecondDescriber{}; }).Extract();
 
  741  template <
typename T>
 
  743    if constexpr (std::is_convertible_v<T, First>) {
 
  750  constexpr value_type GetValuesByIndex(std::size_t index) 
const {
 
  752        [index]() { 
return impl::CaseGetValuesByIndex<First, Second>{index}; });
 
  753    return value_type{result.GetFirst(), result.GetSecond()};
 
  758    using iterator_category = std::input_iterator_tag;
 
  759    using difference_type = std::ptrdiff_t;
 
  761    explicit constexpr iterator(
const TrivialBiMap& map, std::size_t position)
 
  762        : map_{map}, position_{position} {}
 
  764    constexpr bool operator==(
iterator other) 
const {
 
  765      return position_ == other.position_;
 
  768    constexpr bool operator!=(
iterator other) 
const {
 
  769      return position_ != other.position_;
 
  777    constexpr iterator operator++(
int) {
 
  784      return map_.GetValuesByIndex(position_);
 
  788    const TrivialBiMap& map_;
 
  789    std::size_t position_;
 
  794  constexpr iterator cbegin() 
const { 
return begin(); }
 
  795  constexpr iterator cend() 
const { 
return end(); }
 
  798  const BuilderFunc func_;
 
  801template <
typename BuilderFunc>
 
  802TrivialBiMap(BuilderFunc) -> TrivialBiMap<BuilderFunc>;
 
  810template <
typename BuilderFunc>
 
  811class TrivialSet 
final {
 
  813      std::invoke_result_t<
const BuilderFunc&, impl::SwitchTypesDetector>;
 
  816  using First = 
typename TypesPair::first_type;
 
  817  using Second = 
typename TypesPair::second_type;
 
  819  constexpr TrivialSet(BuilderFunc&& func) 
noexcept : func_(std::move(func)) {
 
  820    static_assert(std::is_empty_v<BuilderFunc>,
 
  821                  "Mapping function should not capture variables");
 
  822    static_assert(std::is_trivially_copyable_v<First>,
 
  823                  "First type in Case must be trivially copyable");
 
  824    static_assert(std::is_void_v<Second>,
 
  825                  "Second type in Case should be skipped in utils::TrivialSet");
 
  828  constexpr bool Contains(First value) 
const noexcept {
 
  830               [value]() { 
return impl::SwitchByFirst<First, Second>{value}; })
 
  834  constexpr bool ContainsICase(std::string_view value) 
const noexcept {
 
  835    static_assert(std::is_convertible_v<First, std::string_view>,
 
  836                  "ContainsICase works only with std::string_view");
 
  838    return func_([value]() { 
return impl::SwitchByFirstICase<
void>{value}; })
 
  842  constexpr std::size_t size() 
const noexcept {
 
  843    return func_([]() { 
return impl::CaseCounter{}; }).Extract();
 
  852    return func_([]() { 
return impl::CaseFirstDescriber{}; }).Extract();
 
  857  constexpr std::optional<std::size_t> 
GetIndex(First value) 
const {
 
  858    return func_([value]() { 
return impl::CaseFirstIndexer{value}; }).Extract();
 
  862  const BuilderFunc func_;
 
  865template <
typename BuilderFunc>
 
  866TrivialSet(BuilderFunc) -> TrivialSet<BuilderFunc>;
 
  873template <
typename ExceptionType = 
void, 
typename Value, 
typename BuilderFunc>
 
  875  if constexpr (!std::is_void_v<ExceptionType>) {
 
  876    if (!value.IsString()) {
 
  877      throw ExceptionType(fmt::format(
 
  878          "Invalid value at '{}': expected a string", value.GetPath()));
 
  882  const auto string = value.
template As<std::string>();
 
  883  const auto parsed = map.TryFind(string);
 
  884  if (parsed) 
return *parsed;
 
  887      std::conditional_t<std::is_void_v<ExceptionType>,
 
  888                         typename Value::Exception, ExceptionType>;
 
  890  throw Exception(fmt::format(
 
  891      "Invalid value of {} at '{}': '{}' is not one of {}",
 
  892      compiler::GetTypeName<std::decay_t<
decltype(*parsed)>>(), value.GetPath(),
 
  893      string, map.
template DescribeByType<std::string>()));
 
  901template <
typename Enum, 
typename BuilderFunc>
 
  902std::string_view EnumToStringView(Enum value, TrivialBiMap<BuilderFunc> map) {
 
  903  static_assert(std::is_enum_v<Enum>);
 
  904  if (
const auto string = map.TryFind(value)) 
return *string;
 
  908      fmt::format(
"Invalid value of enum {}: {}", compiler::GetTypeName<Enum>(),
 
  909                  static_cast<std::underlying_type_t<Enum>>(value)));
 
  912template <
typename Selector, 
class Keys, 
typename Values,
 
  913          std::size_t... Indices>
 
  914constexpr auto TrivialBiMapMultiCase(Selector selector, 
const Keys& keys,
 
  915                                     const Values& values,
 
  916                                     std::index_sequence<0, Indices...>) {
 
  917  auto selector2 = selector.Case(std::data(keys)[0], std::data(values)[0]);
 
  919        selector2.Case(std::data(keys)[Indices], std::data(values)[Indices])),
 
  924template <
const auto& Keys, 
const auto& Values>
 
  925struct TrivialBiMapMultiCaseDispatch {
 
  926  template <
class Selector>
 
  927  constexpr auto operator()(Selector selector) 
const {
 
  928    constexpr auto kKeysSize = std::size(Keys);
 
  929    return impl::TrivialBiMapMultiCase(selector(), Keys, Values,
 
  930                                       std::make_index_sequence<kKeysSize>{});
 
  934template <
typename Selector, 
class Values, std::size_t... Indices>
 
  935constexpr auto TrivialSetMultiCase(Selector selector, 
const Values& values,
 
  936                                   std::index_sequence<0, Indices...>) {
 
  937  auto selector2 = selector.Case(std::data(values)[0]);
 
  938  ((selector2 = selector2.Case(std::data(values)[Indices])), ...);
 
  942template <
const auto& Values>
 
  943struct TrivialSetMultiCaseDispatch {
 
  944  template <
class Selector>
 
  945  constexpr auto operator()(Selector selector) 
const {
 
  946    constexpr auto kValuesSize = std::size(Values);
 
  947    return impl::TrivialSetMultiCase(selector(), Values,
 
  948                                     std::make_index_sequence<kValuesSize>{});
 
  955template <
const auto& Keys, 
const auto& Values>
 
  957  static_assert(std::size(Keys) == std::size(Values));
 
  958  static_assert(std::size(Keys) >= 1);
 
  959  return TrivialBiMap(impl::TrivialBiMapMultiCaseDispatch<Keys, Values>{});
 
  962template <
const auto& Values>
 
  963constexpr auto MakeTrivialSet() {
 
  964  return TrivialSet(impl::TrivialSetMultiCaseDispatch<Values>{});