13 #ifndef __STOUT_PROTOBUF_HPP__
14 #define __STOUT_PROTOBUF_HPP__
23 #include <sys/types.h>
26 #include <type_traits>
29 #include <google/protobuf/descriptor.h>
30 #include <google/protobuf/descriptor.pb.h>
31 #include <google/protobuf/message.h>
32 #include <google/protobuf/repeated_field.h>
34 #include <google/protobuf/io/zero_copy_stream_impl.h>
68 if (!message.IsInitialized()) {
69 return Error(message.InitializationErrorString() +
70 " is required but not initialized");
74 uint32_t
size = message.ByteSize();
75 std::string bytes((
char*) &size,
sizeof(size));
79 return Error(
"Failed to write size: " + result.
error());
83 if (!message.SerializeToFileDescriptor(fd.crt())) {
85 if (!message.SerializeToFileDescriptor(fd)) {
87 return Error(
"Failed to write/serialize message");
99 int_fd fd,
const google::protobuf::RepeatedPtrField<T>& messages)
101 foreach (
const T& message, messages) {
112 template <
typename T>
121 return Error(
"Failed to open file '" + path +
"': " + fd.
error());
136 const std::string& path,
137 const google::protobuf::Message& message)
145 return Error(
"Failed to open file '" + path +
"': " + fd.
error());
159 template <
typename T>
163 (void) static_cast<google::protobuf::Message*>(&t);
170 google::protobuf::io::ArrayInputStream stream(
172 static_cast<int>(value.size()));
173 if (!t.ParseFromZeroCopyStream(&stream)) {
174 return Error(
"Failed to deserialize " + t.GetDescriptor()->full_name());
180 template <
typename T>
183 (void) static_cast<const google::protobuf::Message*>(&t);
186 if (!t.SerializeToString(&value)) {
187 return Error(
"Failed to serialize " + t.GetDescriptor()->full_name());
199 template <
typename T>
213 offset = lseek.
get();
223 return Error(
"Failed to read size: " + result.
error());
224 }
else if (result.
isNone()) {
226 }
else if (result.
get().size() <
sizeof(
size)) {
236 "Failed to read size: hit EOF unexpectedly, possible corruption");
240 memcpy((
void*) &size, (
void*) result.
get().data(),
sizeof(
size));
252 return Error(
"Failed to read message: " + result.
error());
262 return Error(
"Failed to read message of size " +
stringify(size) +
263 " bytes: hit EOF unexpectedly, possible corruption");
269 const std::string& data = result.
get();
277 google::protobuf::io::ArrayInputStream stream(
279 static_cast<int>(data.size()));
281 if (!message.ParseFromZeroCopyStream(&stream)) {
286 return Error(
"Failed to deserialize message");
299 template <
typename T>
300 struct Read<google::protobuf::RepeatedPtrField<T>>
303 int_fd fd,
bool ignorePartial,
bool undoFailed)
305 google::protobuf::RepeatedPtrField<T> result;
310 }
else if (message.
isNone()) {
313 result.Add()->CopyFrom(message.
get());
333 template <
typename T>
342 template <
typename T>
351 return Error(
"Failed to open file '" + path +
"': " + fd.
error());
375 google::protobuf::Message* message,
379 struct Parser : boost::static_visitor<Try<Nothing>>
381 Parser(google::protobuf::Message* _message,
382 const google::protobuf::FieldDescriptor* _field)
384 reflection(message->GetReflection()),
389 switch (field->type()) {
390 case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
396 if (field->is_repeated()) {
397 return parse(reflection->AddMessage(message, field), object);
399 return parse(reflection->MutableMessage(message, field), object);
403 return Error(
"Not expecting a JSON object for field '" +
404 field->name() +
"'");
411 switch (field->type()) {
412 case google::protobuf::FieldDescriptor::TYPE_STRING:
413 if (field->is_repeated()) {
414 reflection->AddString(message, field,
string.value);
416 reflection->SetString(message, field,
string.value);
419 case google::protobuf::FieldDescriptor::TYPE_BYTES: {
423 return Error(
"Failed to base64 decode bytes field"
424 " '" + field->name() +
"': " + decode.
error());
427 if (field->is_repeated()) {
428 reflection->AddString(message, field, decode.
get());
430 reflection->SetString(message, field, decode.
get());
434 case google::protobuf::FieldDescriptor::TYPE_ENUM: {
435 const google::protobuf::EnumValueDescriptor* descriptor =
436 field->enum_type()->FindValueByName(
string.value);
438 if (descriptor ==
nullptr) {
439 if (field->is_required()) {
440 return Error(
"Failed to find enum for '" +
string.value +
"'");
454 if (field->is_repeated()) {
455 reflection->AddEnum(message, field, descriptor);
457 reflection->SetEnum(message, field, descriptor);
462 return Error(
"Not expecting a JSON string for field '" +
463 field->name() +
"'");
470 switch (field->type()) {
471 case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
472 if (field->is_repeated()) {
473 reflection->AddDouble(message, field, number.
as<
double>());
475 reflection->SetDouble(message, field, number.
as<
double>());
478 case google::protobuf::FieldDescriptor::TYPE_FLOAT:
479 if (field->is_repeated()) {
480 reflection->AddFloat(message, field, number.
as<
float>());
482 reflection->SetFloat(message, field, number.
as<
float>());
485 case google::protobuf::FieldDescriptor::TYPE_INT64:
486 case google::protobuf::FieldDescriptor::TYPE_SINT64:
487 case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
488 if (field->is_repeated()) {
489 reflection->AddInt64(message, field, number.
as<int64_t>());
491 reflection->SetInt64(message, field, number.
as<int64_t>());
494 case google::protobuf::FieldDescriptor::TYPE_UINT64:
495 case google::protobuf::FieldDescriptor::TYPE_FIXED64:
496 if (field->is_repeated()) {
497 reflection->AddUInt64(message, field, number.
as<uint64_t>());
499 reflection->SetUInt64(message, field, number.
as<uint64_t>());
502 case google::protobuf::FieldDescriptor::TYPE_INT32:
503 case google::protobuf::FieldDescriptor::TYPE_SINT32:
504 case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
505 if (field->is_repeated()) {
506 reflection->AddInt32(message, field, number.
as<int32_t>());
508 reflection->SetInt32(message, field, number.
as<int32_t>());
511 case google::protobuf::FieldDescriptor::TYPE_UINT32:
512 case google::protobuf::FieldDescriptor::TYPE_FIXED32:
513 if (field->is_repeated()) {
514 reflection->AddUInt32(message, field, number.
as<uint32_t>());
516 reflection->SetUInt32(message, field, number.
as<uint32_t>());
520 return Error(
"Not expecting a JSON number for field '" +
521 field->name() +
"'");
528 if (!field->is_repeated()) {
529 return Error(
"Not expecting a JSON array for field '" +
530 field->name() +
"'");
535 boost::apply_visitor(
Parser(message, field), value);
547 switch (field->type()) {
548 case google::protobuf::FieldDescriptor::TYPE_BOOL:
549 if (field->is_repeated()) {
550 reflection->AddBool(message, field,
boolean.value);
552 reflection->SetBool(message, field,
boolean.value);
556 return Error(
"Not expecting a JSON boolean for field '" +
557 field->name() +
"'");
571 google::protobuf::Message* message;
572 const google::protobuf::Reflection* reflection;
573 const google::protobuf::FieldDescriptor* field;
578 google::protobuf::Message* message,
584 const google::protobuf::FieldDescriptor* field =
585 message->GetDescriptor()->FindFieldByName(name);
587 if (field !=
nullptr) {
589 boost::apply_visitor(
Parser(message, field), value);
604 template <
typename T>
609 static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
610 "T must be a protobuf message");
612 const JSON::Object*
object = boost::get<JSON::Object>(&value);
613 if (
object ==
nullptr) {
614 return Error(
"Expecting a JSON object");
624 if (!message.IsInitialized()) {
625 return Error(
"Missing required fields: " +
626 message.InitializationErrorString());
639 template <
typename T>
640 struct Parse<google::protobuf::RepeatedPtrField<T>>
645 static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
646 "T must be a protobuf message");
648 const JSON::Array* array = boost::get<JSON::Array>(&value);
649 if (array ==
nullptr) {
650 return Error(
"Expecting a JSON array");
653 google::protobuf::RepeatedPtrField<T> collection;
654 collection.Reserve(static_cast<int>(array->
values.size()));
663 collection.Add()->CopyFrom(message.
get());
680 template <
typename T>
703 using google::protobuf::FieldDescriptor;
705 const google::protobuf::Message& message =
protobuf;
707 const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
708 const google::protobuf::Reflection* reflection = message.GetReflection();
714 int fieldCount = descriptor->field_count();
715 std::vector<const FieldDescriptor*> fields;
716 fields.reserve(fieldCount);
717 for (
int i = 0; i < fieldCount; ++i) {
718 const FieldDescriptor* field = descriptor->field(i);
719 if (field->is_repeated()) {
720 if (reflection->FieldSize(message, field) > 0) {
722 fields.push_back(field);
725 reflection->HasField(message, field) ||
726 (field->has_default_value() && !field->options().deprecated())) {
728 fields.push_back(field);
732 foreach (
const FieldDescriptor* field, fields) {
733 if (field->is_repeated()) {
737 int fieldSize = reflection->FieldSize(message, field);
738 for (
int i = 0; i < fieldSize; ++i) {
739 switch (field->cpp_type()) {
740 case FieldDescriptor::CPPTYPE_BOOL:
742 reflection->GetRepeatedBool(message, field, i));
744 case FieldDescriptor::CPPTYPE_INT32:
746 reflection->GetRepeatedInt32(message, field, i));
748 case FieldDescriptor::CPPTYPE_INT64:
750 reflection->GetRepeatedInt64(message, field, i));
752 case FieldDescriptor::CPPTYPE_UINT32:
754 reflection->GetRepeatedUInt32(message, field, i));
756 case FieldDescriptor::CPPTYPE_UINT64:
758 reflection->GetRepeatedUInt64(message, field, i));
760 case FieldDescriptor::CPPTYPE_FLOAT:
762 reflection->GetRepeatedFloat(message, field, i));
764 case FieldDescriptor::CPPTYPE_DOUBLE:
766 reflection->GetRepeatedDouble(message, field, i));
768 case FieldDescriptor::CPPTYPE_MESSAGE:
770 reflection->GetRepeatedMessage(message, field, i)));
772 case FieldDescriptor::CPPTYPE_ENUM:
774 reflection->GetRepeatedEnum(message, field, i)->name());
776 case FieldDescriptor::CPPTYPE_STRING:
777 const std::string& s = reflection->GetRepeatedStringReference(
778 message, field, i,
nullptr);
779 if (field->type() == FieldDescriptor::TYPE_BYTES) {
789 switch (field->cpp_type()) {
790 case FieldDescriptor::CPPTYPE_BOOL:
791 writer->
field(field->name(), reflection->GetBool(message, field));
793 case FieldDescriptor::CPPTYPE_INT32:
794 writer->
field(field->name(), reflection->GetInt32(message, field));
796 case FieldDescriptor::CPPTYPE_INT64:
797 writer->
field(field->name(), reflection->GetInt64(message, field));
799 case FieldDescriptor::CPPTYPE_UINT32:
800 writer->
field(field->name(), reflection->GetUInt32(message, field));
802 case FieldDescriptor::CPPTYPE_UINT64:
803 writer->
field(field->name(), reflection->GetUInt64(message, field));
805 case FieldDescriptor::CPPTYPE_FLOAT:
806 writer->
field(field->name(), reflection->GetFloat(message, field));
808 case FieldDescriptor::CPPTYPE_DOUBLE:
809 writer->
field(field->name(), reflection->GetDouble(message, field));
811 case FieldDescriptor::CPPTYPE_MESSAGE:
813 field->name(),
Protobuf(reflection->GetMessage(message, field)));
815 case FieldDescriptor::CPPTYPE_ENUM:
817 field->name(), reflection->GetEnum(message, field)->name());
819 case FieldDescriptor::CPPTYPE_STRING:
820 const std::string& s = reflection->GetStringReference(
821 message, field,
nullptr);
822 if (field->type() == FieldDescriptor::TYPE_BYTES) {
825 writer->
field(field->name(), s);
840 const google::protobuf::Descriptor* descriptor = message.GetDescriptor();
841 const google::protobuf::Reflection* reflection = message.GetReflection();
847 std::vector<const google::protobuf::FieldDescriptor*> fields;
848 fields.reserve(descriptor->field_count());
849 for (
int i = 0; i < descriptor->field_count(); i++) {
850 const google::protobuf::FieldDescriptor* field = descriptor->field(i);
851 if (field->is_repeated()) {
852 if (reflection->FieldSize(message, descriptor->field(i)) > 0) {
854 fields.push_back(field);
857 reflection->HasField(message, field) ||
858 (field->has_default_value() && !field->options().deprecated())) {
860 fields.push_back(field);
864 foreach (
const google::protobuf::FieldDescriptor* field, fields) {
865 if (field->is_repeated()) {
867 int fieldSize = reflection->FieldSize(message, field);
868 array.
values.reserve(fieldSize);
869 for (
int i = 0; i < fieldSize; ++i) {
870 switch (field->type()) {
871 case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
873 reflection->GetRepeatedDouble(message, field, i)));
875 case google::protobuf::FieldDescriptor::TYPE_FLOAT:
877 reflection->GetRepeatedFloat(message, field, i)));
879 case google::protobuf::FieldDescriptor::TYPE_INT64:
880 case google::protobuf::FieldDescriptor::TYPE_SINT64:
881 case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
883 reflection->GetRepeatedInt64(message, field, i)));
885 case google::protobuf::FieldDescriptor::TYPE_UINT64:
886 case google::protobuf::FieldDescriptor::TYPE_FIXED64:
888 reflection->GetRepeatedUInt64(message, field, i)));
890 case google::protobuf::FieldDescriptor::TYPE_INT32:
891 case google::protobuf::FieldDescriptor::TYPE_SINT32:
892 case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
894 reflection->GetRepeatedInt32(message, field, i)));
896 case google::protobuf::FieldDescriptor::TYPE_UINT32:
897 case google::protobuf::FieldDescriptor::TYPE_FIXED32:
899 reflection->GetRepeatedUInt32(message, field, i)));
901 case google::protobuf::FieldDescriptor::TYPE_BOOL:
902 if (reflection->GetRepeatedBool(message, field, i)) {
908 case google::protobuf::FieldDescriptor::TYPE_STRING:
910 reflection->GetRepeatedString(message, field, i)));
912 case google::protobuf::FieldDescriptor::TYPE_BYTES:
914 reflection->GetRepeatedString(message, field, i))));
916 case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
918 reflection->GetRepeatedMessage(message, field, i)));
920 case google::protobuf::FieldDescriptor::TYPE_ENUM:
922 reflection->GetRepeatedEnum(message, field, i)->name()));
924 case google::protobuf::FieldDescriptor::TYPE_GROUP:
927 ABORT(
"Unhandled protobuf field type: " +
931 object.values[field->name()] = array;
933 switch (field->type()) {
934 case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
935 object.
values[field->name()] =
938 case google::protobuf::FieldDescriptor::TYPE_FLOAT:
939 object.values[field->name()] =
942 case google::protobuf::FieldDescriptor::TYPE_INT64:
943 case google::protobuf::FieldDescriptor::TYPE_SINT64:
944 case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
945 object.values[field->name()] =
948 case google::protobuf::FieldDescriptor::TYPE_UINT64:
949 case google::protobuf::FieldDescriptor::TYPE_FIXED64:
950 object.values[field->name()] =
953 case google::protobuf::FieldDescriptor::TYPE_INT32:
954 case google::protobuf::FieldDescriptor::TYPE_SINT32:
955 case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
956 object.values[field->name()] =
959 case google::protobuf::FieldDescriptor::TYPE_UINT32:
960 case google::protobuf::FieldDescriptor::TYPE_FIXED32:
961 object.values[field->name()] =
964 case google::protobuf::FieldDescriptor::TYPE_BOOL:
965 if (reflection->GetBool(message, field)) {
971 case google::protobuf::FieldDescriptor::TYPE_STRING:
972 object.values[field->name()] =
975 case google::protobuf::FieldDescriptor::TYPE_BYTES:
979 case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
980 object.values[field->name()] =
981 protobuf(reflection->GetMessage(message, field));
983 case google::protobuf::FieldDescriptor::TYPE_ENUM:
984 object.
values[field->name()] =
985 JSON::String(reflection->GetEnum(message, field)->name());
987 case google::protobuf::FieldDescriptor::TYPE_GROUP:
990 ABORT(
"Unhandled protobuf field type: " +
1000 template <
typename T>
1003 static_assert(std::is_convertible<T*, google::protobuf::Message*>::value,
1004 "T must be a protobuf message");
1007 array.
values.reserve(repeated.size());
1008 foreach (
const T& elem, repeated) {
1017 #endif // __STOUT_PROTOBUF_HPP__
T as() const
Definition: json.hpp:116
Try< std::string > decode(const std::string &s)
Decode a string that is Base64-encoded with the standard Base64 alphabet.
Definition: base64.hpp:172
Try< Nothing > operator()(const JSON::Object &object) const
Definition: protobuf.hpp:387
bool isNone() const
Definition: result.hpp:112
#define O_WRONLY
Definition: fcntl.hpp:26
Definition: nothing.hpp:16
Definition: errorbase.hpp:35
#define ABORT(...)
Definition: abort.hpp:40
#define O_TRUNC
Definition: fcntl.hpp:29
Try< Bytes > size(const std::string &path, const FollowSymlink follow=FollowSymlink::FOLLOW_SYMLINK)
Definition: stat.hpp:100
const mode_t S_IRGRP
Definition: windows.hpp:319
#define O_APPEND
Definition: fcntl.hpp:30
Try< T > parse(const JSON::Value &value)
Definition: protobuf.hpp:681
Result< Classifier > decode(const Netlink< struct rtnl_cls > &cls)
Try< Nothing > operator()(const JSON::Number &number) const
Definition: protobuf.hpp:468
Definition: protobuf.hpp:605
#define O_CLOEXEC
Definition: fcntl.hpp:31
ssize_t write(const WindowsFD &fd, const void *data, size_t size)
Definition: write.hpp:29
static Result< T > error(const std::string &message)
Definition: result.hpp:53
Try< google::protobuf::RepeatedPtrField< T > > operator()(const JSON::Value &value)
Definition: protobuf.hpp:642
const mode_t S_IWUSR
Definition: windows.hpp:312
Result< google::protobuf::RepeatedPtrField< T > > operator()(int_fd fd, bool ignorePartial, bool undoFailed)
Definition: protobuf.hpp:302
Try< T > operator()(const JSON::Value &value)
Definition: protobuf.hpp:607
Try< off_t > lseek(int_fd fd, off_t offset, int whence)
Definition: lseek.hpp:29
Definition: protobuf.hpp:379
std::string encode(const std::string &s)
Encode a string to Base64 with the standard Base64 alphabet.
Definition: base64.hpp:159
Try< Nothing > operator()(const JSON::String &string) const
Definition: protobuf.hpp:409
#define O_CREAT
Definition: fcntl.hpp:28
const mode_t S_IRUSR
Definition: windows.hpp:311
Try< std::string > serialize(const T &t)
Definition: protobuf.hpp:181
std::map< std::string, Value > values
Definition: json.hpp:190
Definition: result.hpp:40
Try< Nothing > write(int_fd fd, const google::protobuf::Message &message)
Definition: protobuf.hpp:66
Try< Nothing > operator()(const JSON::Array &array) const
Definition: protobuf.hpp:526
void field(const std::string &key, const T &value)
Definition: jsonify.hpp:435
Definition: protobuf.hpp:200
Try< Nothing > close(int fd)
Definition: close.hpp:24
Option< T > max(const Option< T > &left, const Option< T > &right)
Definition: option.hpp:199
void json(JSON::ObjectWriter *writer, const Task &task)
Try< Nothing > operator()(const JSON::Boolean &boolean) const
Definition: protobuf.hpp:545
Definition: jsonify.hpp:418
Try< T > deserialize(const std::string &value)
Definition: protobuf.hpp:160
#define foreachpair(KEY, VALUE, ELEMS)
Definition: foreach.hpp:51
std::vector< Value > values
Definition: json.hpp:199
const T & get() const
Definition: result.hpp:115
Result< T > read(int_fd fd, bool ignorePartial=false, bool undoFailed=false)
Definition: protobuf.hpp:334
Result< T > operator()(int_fd fd, bool ignorePartial, bool undoFailed)
Definition: protobuf.hpp:202
static Try error(const E &e)
Definition: try.hpp:42
Result< std::string > read(int_fd fd, size_t size)
Definition: read.hpp:50
Parser(google::protobuf::Message *_message, const google::protobuf::FieldDescriptor *_field)
Definition: protobuf.hpp:381
Try< Nothing > operator()(const JSON::Null &) const
Definition: protobuf.hpp:562
Definition: protobuf.hpp:692
Try< Nothing > parse(google::protobuf::Message *message, const JSON::Object &object)
Definition: protobuf.hpp:577
bool isError() const
Definition: try.hpp:71
Try< int_fd > open(const std::string &path, int oflag, mode_t mode=0)
Definition: open.hpp:39
Object protobuf(const google::protobuf::Message &message)
Definition: protobuf.hpp:836
bool isSome() const
Definition: result.hpp:111
bool isError() const
Definition: result.hpp:113
Try< Nothing > append(const std::string &path, const google::protobuf::Message &message)
Definition: protobuf.hpp:135
int int_fd
Definition: int_fd.hpp:35
std::string stringify(int flags)
#define O_RDONLY
Definition: fcntl.hpp:25
const mode_t S_IROTH
Definition: windows.hpp:327
const T & get() const
Definition: try.hpp:73
constexpr const char * name
Definition: shell.hpp:41
Definition: representation.hpp:72
Definition: jsonify.hpp:384