in include/ylt/standalone/iguana/pb_writer.hpp [312:457]
IGUANA_INLINE void to_proto_impl(
Stream& out, std::unordered_map<std::string_view, std::string>& map,
std::string_view field_name = "", uint32_t field_no = 0) {
std::string sub_str;
using T = std::remove_const_t<std::remove_reference_t<Type>>;
if constexpr (ylt_refletable_v<T> || is_custom_reflection_v<T>) {
constexpr auto name = type_string<T>();
out.append("message ").append(name).append(" {\n");
static T t;
static auto tuple = get_pb_members_tuple(t);
constexpr size_t SIZE = std::tuple_size_v<std::decay_t<decltype(tuple)>>;
for_each_n(
[&out, &sub_str, &map](auto i) mutable {
using field_type =
std::tuple_element_t<decltype(i)::value,
std::decay_t<decltype(tuple)>>;
auto value = std::get<decltype(i)::value>(tuple);
using U = typename field_type::value_type;
using sub_type = typename field_type::sub_type;
if constexpr (ylt_refletable_v<U>) {
constexpr auto str_type = get_type_string<U>();
build_proto_field(
out, str_type,
{value.field_name.data(), value.field_name.size()},
value.field_no);
build_sub_proto<U>(map, str_type, sub_str);
}
else if constexpr (variant_v<U>) {
constexpr size_t var_size = std::variant_size_v<U>;
constexpr auto offset =
get_variant_index<U, sub_type, var_size - 1>();
if (offset == 0) {
out.append(" oneof ");
out.append(value.field_name.data(), value.field_name.size())
.append(" {\n");
}
constexpr auto str_type = get_type_string<sub_type>();
std::string field_name = " one_of_";
field_name.append(str_type);
out.append(" ");
build_proto_field(out, str_type, field_name, value.field_no);
if constexpr (ylt_refletable_v<sub_type>) {
build_sub_proto<sub_type>(map, str_type, sub_str);
}
if (offset == var_size - 1) {
out.append(" }\n");
}
}
else {
to_proto_impl<U>(out, map,
{value.field_name.data(), value.field_name.size()},
value.field_no);
}
},
std::make_index_sequence<SIZE>{});
out.append("}\r\n\r\n");
}
else if constexpr (is_sequence_container<T>::value) {
out.append(" repeated");
using item_type = typename T::value_type;
if constexpr (is_lenprefix_v<item_type>) {
// non-packed
if constexpr (ylt_refletable_v<item_type>) {
constexpr auto str_type = get_type_string<item_type>();
build_proto_field(out, str_type, field_name, field_no);
build_sub_proto<item_type>(map, str_type, sub_str);
}
else {
to_proto_impl<item_type>(out, map, field_name, field_no);
}
}
else {
out.append(" ");
numeric_to_proto<item_type>(out, field_name, field_no);
}
}
else if constexpr (is_map_container<T>::value) {
out.append(" map<");
using first_type = typename T::key_type;
using second_type = typename T::mapped_type;
constexpr auto str_first = get_type_string<first_type>();
constexpr auto str_second = get_type_string<second_type>();
out.append(str_first).append(", ").append(str_second).append(">");
build_proto_field<1>(out, "", field_name, field_no);
if constexpr (ylt_refletable_v<second_type>) {
constexpr auto str_type = get_type_string<second_type>();
build_sub_proto<second_type>(map, str_type, sub_str);
}
}
else if constexpr (optional_v<T>) {
to_proto_impl<typename T::value_type>(
out, map, {field_name.data(), field_name.size()}, field_no);
}
else if constexpr (std::is_same_v<T, std::string> ||
std::is_same_v<T, std::string_view>) {
build_proto_field(out, "string ", field_name, field_no);
}
else if constexpr (enum_v<T>) {
constexpr auto str_type = get_type_string<T>();
static constexpr auto enum_to_str = get_enum_map<false, std::decay_t<T>>();
if constexpr (bool_v<decltype(enum_to_str)>) {
build_proto_field(out, "int32", field_name, field_no);
}
else {
static_assert(enum_to_str.size() > 0, "empty enum not allowed");
static_assert((int)(enum_to_str.begin()->first) == 0,
"the first enum value must be zero in proto3");
build_proto_field(out, str_type, field_name, field_no);
if (map.find(str_type) == map.end()) {
sub_str.append("enum ").append(str_type).append(" {\n");
for (auto& [k, field_name] : enum_to_str) {
std::string_view name{field_name.data(), field_name.size()};
size_t pos = name.rfind("::");
if (pos != std::string_view::npos) {
name = name.substr(pos + 2);
}
sub_str.append(" ")
.append(name)
.append(" = ")
.append(std::to_string(static_cast<std::underlying_type_t<T>>(k)))
.append(";\n");
}
sub_str.append("}\r\n\r\n");
map.emplace(str_type, std::move(sub_str));
}
}
}
else {
out.append(" ");
numeric_to_proto<Type>(out, field_name, field_no);
}
}