2828
2929#include < absl/container/flat_hash_set.h>
3030#include < absl/strings/ascii.h>
31+ #include < absl/strings/escaping.h>
3132#include < boost/algorithm/string/trim.hpp>
3233#include < boost/container/flat_set.hpp>
3334#include < boost/range/combine.hpp>
7273
7374#include < algorithm>
7475#include < charconv>
76+ #include < concepts>
77+ #include < cstdint>
7578#include < functional>
7679#include < optional>
7780#include < ranges>
7881#include < string_view>
7982#include < unordered_set>
8083
84+ namespace {
85+
86+ // Protobuf string values need to be quoted and escaped
87+ // as they may contain characters like '\'
88+ auto pb_string_value (const std::string_view v) {
89+ return fmt::format (" \" {}\" " , absl::CEscape (v));
90+ }
91+
92+ } // namespace
93+
8194struct indent_formatter : fmt::formatter<std::string_view> {
8295 using Base = fmt::formatter<std::string_view>;
8396 constexpr auto parse (fmt::format_parse_context& ctx) {
@@ -133,10 +146,18 @@ struct fmt::formatter<google::protobuf::UninterpretedOption>
133146 const google::protobuf::UninterpretedOption& option,
134147 format_context& ctx) const {
135148 const auto fmt = [&](const auto & val) {
136- if (option.has_string_value ()) {
137- return fmt::format_to (
138- ctx.out (), " {} = \" {}\" " , fmt::join (option.name (), " ." ), val);
139- } else if (option.has_aggregate_value ()) {
149+ if constexpr (std::convertible_to<
150+ std::decay_t <decltype (val)>,
151+ std::string_view>) {
152+ if (option.has_string_value ()) {
153+ return fmt::format_to (
154+ ctx.out (),
155+ " {} = {}" ,
156+ fmt::join (option.name (), " ." ),
157+ pb_string_value (val));
158+ }
159+ }
160+ if (option.has_aggregate_value ()) {
140161 return fmt::format_to (
141162 ctx.out (),
142163 " {} = {{{}\n {:{}}}}" ,
@@ -255,6 +276,10 @@ auto field_options() {
255276 " packed" , &pb::FieldOptions::has_packed, &pb::FieldOptions::packed},
256277 field_option{
257278 " lazy" , &pb::FieldOptions::has_lazy, &pb::FieldOptions::lazy},
279+ field_option{
280+ " unverified_lazy" ,
281+ &pb::FieldOptions::has_unverified_lazy,
282+ &pb::FieldOptions::unverified_lazy},
258283 field_option{
259284 " weak" , &pb::FieldOptions::has_weak, &pb::FieldOptions::weak},
260285 field_option{
@@ -919,7 +944,8 @@ struct protobuf_schema_definition::impl {
919944 }
920945 if (field.has_json_name ()) {
921946 maybe_print_seperator ();
922- fmt::print (os, " json_name = \" {}\" " , field.json_name ());
947+ fmt::print (
948+ os, " json_name = {}" , pb_string_value (field.json_name ()));
923949 }
924950 if (field.has_options ()) {
925951 const auto & options = field.options ();
@@ -1075,11 +1101,12 @@ struct protobuf_schema_definition::impl {
10751101
10761102 if (decl.has_full_name ()) {
10771103 maybe_print_seperator ();
1078- fmt::print (os, " {}: \" {}\" " , " full_name" , decl.full_name ());
1104+ fmt::print (
1105+ os, " {}: {}" , " full_name" , pb_string_value (decl.full_name ()));
10791106 }
10801107 if (decl.has_type ()) {
10811108 maybe_print_seperator ();
1082- fmt::print (os, " {}: \" {} \" " , " type" , decl.type ());
1109+ fmt::print (os, " {}: {} " , " type" , pb_string_value ( decl.type () ));
10831110 }
10841111 if (decl.has_number ()) {
10851112 maybe_print_seperator ();
@@ -1161,12 +1188,16 @@ struct protobuf_schema_definition::impl {
11611188 }
11621189 auto reserved_names = maybe_sorted (message.reserved_name ());
11631190 if (!reserved_names.empty ()) {
1191+ const auto to_debug_string = [](const std::string_view strv) {
1192+ return fmt::format (" {}" , pb_string_value (strv));
1193+ };
11641194 fmt::print (
11651195 os,
1166- " {:{}}reserved \" {} \" ;\n " ,
1196+ " {:{}}reserved {} ;\n " ,
11671197 " " ,
11681198 indent + 2 ,
1169- fmt::join (reserved_names, " \" , \" " ));
1199+ fmt::join (
1200+ reserved_names | std::views::transform (to_debug_string), " , " ));
11701201 }
11711202 if (!reserved_range.empty () || !reserved_names.empty ()) {
11721203 fmt::print (os, " \n " );
@@ -1287,7 +1318,12 @@ struct protobuf_schema_definition::impl {
12871318 fmt::print (os, " ;\n " );
12881319 }
12891320 for (const auto & value : maybe_sorted (enum_proto.reserved_name ())) {
1290- fmt::print (os, " {:{}}reserved \" {}\" ;\n " , " " , indent + 2 , value);
1321+ fmt::print (
1322+ os,
1323+ " {:{}}reserved {};\n " ,
1324+ " " ,
1325+ indent + 2 ,
1326+ pb_string_value (value));
12911327 }
12921328 if (enum_proto.options ().has_allow_alias ()) {
12931329 fmt::print (
@@ -1310,14 +1346,16 @@ struct protobuf_schema_definition::impl {
13101346 for (const auto & option : uninterpreted_options) {
13111347 fmt::print (os, " {:{}}option {};\n " , " " , indent + 2 , option);
13121348 }
1313- std::optional<std::decay_t <decltype (enum_proto.value ())>> values;
1314- if (is_normalized) {
1315- values = enum_proto.value ();
1316- std::ranges::sort (values.value (), std::less{}, [](const auto & v) {
1317- return std::pair<int , std::string_view>{v.number (), v.name ()};
1318- });
1319- }
1320- for (const auto & value : values.value_or (enum_proto.value ())) {
1349+ const auto values = maybe_sorted (
1350+ enum_proto.value (), std::less{}, [](const auto & v) {
1351+ // In proto3, enums are open and open enums need to
1352+ // have the first field being equal to zero. By casting
1353+ // to an unsigned integer for sorting, all the negative
1354+ // fields will be at the end, after all the positives.
1355+ return std::pair<uint32_t , std::string_view>{
1356+ static_cast <uint32_t >(v.number ()), v.name ()};
1357+ });
1358+ for (const auto & value : values) {
13211359 fmt::print (
13221360 os, " {:{}}{} = {}" , " " , indent + 2 , value.name (), value.number ());
13231361 if (value.has_options ()) {
@@ -1427,7 +1465,7 @@ struct protobuf_schema_definition::impl {
14271465 first_option = false ;
14281466 };
14291467 auto prints = [&](std::string_view name, const auto & val) {
1430- fmt::print (os, " option {} = \" {} \" ;\n " , name, val);
1468+ fmt::print (os, " option {} = {} ;\n " , name, pb_string_value ( val) );
14311469 first_option = false ;
14321470 };
14331471 if (options.has_cc_enable_arenas ()) {
@@ -1544,7 +1582,7 @@ struct protobuf_schema_definition::impl {
15441582
15451583 auto print_deps = [&](const auto & view, std::string_view type) {
15461584 for (const auto & dep : view) {
1547- fmt::print (os, " import {}\" {} \" ;\n " , type, dep);
1585+ fmt::print (os, " import {}{} ;\n " , type, pb_string_value ( dep) );
15481586 }
15491587 };
15501588
@@ -1559,7 +1597,7 @@ struct protobuf_schema_definition::impl {
15591597 const pb::FileDescriptor& descriptor) const {
15601598 auto syntax = fdp.has_syntax () ? fdp.syntax () : " proto2" ;
15611599 std::string_view edition = syntax;
1562- fmt::print (os, " syntax = \" {} \" ;\n " , syntax);
1600+ fmt::print (os, " syntax = {} ;\n " , pb_string_value ( syntax) );
15631601
15641602 if (fdp.has_package () && !fdp.package ().empty ()) {
15651603 fmt::print (os, " package {};\n " , fdp.package ());
0 commit comments