Skip to content

Commit c2bb343

Browse files
authored
feat: add support for dumping variant to string (#526)
This allows Message and Variant contents serialization to a string -- based on `sd_bus_message_dump` sd-bus API function.
1 parent 9f3e89e commit c2bb343

File tree

5 files changed

+93
-2
lines changed

5 files changed

+93
-2
lines changed

.clang-tidy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ CheckOptions:
5555
- key: hicpp-move-const-arg.CheckTriviallyCopyableMove
5656
value: '0'
5757
- key: misc-include-cleaner.IgnoreHeaders
58-
value: 'systemd/.*|sdbus-c\+\+/.*|gtest/.*|gmock/.*|bits/chrono.h|bits/basic_string.h|time.h|poll.h|stdlib.h'
58+
value: 'systemd/.*|sdbus-c\+\+/.*|gtest/.*|gmock/.*|bits/chrono.h|bits/basic_string.h|time.h|poll.h|stdlib.h|stdio.h'
5959
- key: readability-simplify-boolean-expr.IgnoreMacros
6060
value: '1'
6161
- key: cppcoreguidelines-rvalue-reference-param-not-moved.IgnoreUnnamedParams

include/sdbus-c++/Message.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,15 @@ namespace sdbus {
228228
void seal();
229229
void rewind(bool complete);
230230

231+
enum class DumpFlags : uint64_t // NOLINT(performance-enum-size): using size from sd-bus
232+
{
233+
Default = 0ULL,
234+
WithHeader = 1ULL << 0,
235+
SubtreeOnly = 1ULL << 1,
236+
SubtreeOnlyWithHeader = WithHeader | SubtreeOnly
237+
};
238+
[[nodiscard]] std::string dumpToString(DumpFlags flags) const;
239+
231240
pid_t getCredsPid() const;
232241
uid_t getCredsUid() const;
233242
uid_t getCredsEuid() const;
@@ -260,7 +269,6 @@ namespace sdbus {
260269

261270
friend Factory;
262271

263-
264272
void* msg_{};
265273
internal::IConnection* connection_{};
266274
mutable bool ok_{true};

include/sdbus-c++/Types.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,13 @@ namespace sdbus {
103103
return val;
104104
}
105105

106+
[[nodiscard]] std::string dumpToString() const
107+
{
108+
msg_.rewind(false);
109+
110+
return msg_.dumpToString(Message::DumpFlags::SubtreeOnly);
111+
}
112+
106113
// Only allow conversion operator for true D-Bus type representations in C++
107114
// NOLINTNEXTLINE(modernize-use-constraints): TODO for future: Use `requires signature_of<_ValueType>::is_valid` (when we stop supporting C++17 in public API)
108115
template <typename ValueType, typename = std::enable_if_t<signature_of<ValueType>::is_valid>>

src/Message.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
#include "ScopeGuard.h"
3636

3737
#include <cassert>
38+
#include <cerrno>
3839
#include <cstdint> // int16_t, uint64_t, ...
40+
#include <cstdio>
3941
#include <cstdlib> // atexit
4042
#include <cstring>
4143
#include <string>
@@ -628,6 +630,28 @@ void Message::rewind(bool complete)
628630
SDBUS_THROW_ERROR_IF(r < 0, "Failed to rewind the message", -r);
629631
}
630632

633+
std::string Message::dumpToString(DumpFlags flags) const
634+
{
635+
#if LIBSYSTEMD_VERSION>=245 && !defined(SDBUS_basu)
636+
char* buffer{};
637+
SCOPE_EXIT{ free(buffer); }; // NOLINT(cppcoreguidelines-no-malloc,hicpp-no-malloc,cppcoreguidelines-owning-memory)
638+
size_t size{};
639+
640+
const std::unique_ptr<FILE, int(*)(FILE*)> stream{open_memstream(&buffer, &size), fclose};
641+
SDBUS_THROW_ERROR_IF(!stream, "Failed to open memory stream", errno);
642+
643+
auto r = sd_bus_message_dump(static_cast<sd_bus_message*>(msg_), stream.get(), static_cast<uint64_t>(flags));
644+
SDBUS_THROW_ERROR_IF(r < 0, "Failed to dump the message", -r);
645+
646+
(void)fflush(stream.get());
647+
648+
return {buffer, size};
649+
#else
650+
(void)flags;
651+
throw Error(Error::Name{SD_BUS_ERROR_NOT_SUPPORTED}, "Dumping sd-bus message not supported by underlying version of libsystemd");
652+
#endif
653+
}
654+
631655
const char* Message::getInterfaceName() const
632656
{
633657
return sd_bus_message_get_interface(static_cast<sd_bus_message*>(msg_));

tests/unittests/Types_test.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545

4646
using ::testing::Eq;
4747
using ::testing::Gt;
48+
using ::testing::HasSubstr;
4849
using namespace std::string_literals;
4950

5051
namespace
@@ -148,6 +149,24 @@ TEST(ASimpleVariant, ReturnsTheSimpleValueWhenAsked)
148149
ASSERT_THAT(variant.get<int>(), Eq(value));
149150
}
150151

152+
#ifndef SDBUS_basu // Dumping message or variant to a string is not supported on basu backend
153+
TEST(ASimpleVariant, CanBeDumpedToAString)
154+
{
155+
const int value = 5;
156+
const sdbus::Variant variant(value);
157+
158+
// This should produce something like:
159+
// VARIANT "i" {
160+
// INT32 5;
161+
// };
162+
const auto str = variant.dumpToString();
163+
164+
EXPECT_THAT(str, ::HasSubstr("VARIANT \"i\""));
165+
EXPECT_THAT(str, ::HasSubstr("INT32"));
166+
EXPECT_THAT(str, ::HasSubstr("5"));
167+
}
168+
#endif // SDBUS_basu
169+
151170
TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked)
152171
{
153172
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
@@ -158,6 +177,39 @@ TEST(AComplexVariant, ReturnsTheComplexValueWhenAsked)
158177
ASSERT_THAT(variant.get<ComplexType>(), Eq(value));
159178
}
160179

180+
#ifndef SDBUS_basu // Dumping message or variant to a string is not supported on basu backend
181+
TEST(AComplexVariant, CanBeDumpedToAString)
182+
{
183+
using ComplexType = std::map<uint64_t, std::vector<sdbus::Struct<std::string, double>>>;
184+
const ComplexType value{ {ANY_UINT64, ComplexType::mapped_type{{"hello"s, ANY_DOUBLE}, {"world"s, ANY_DOUBLE}}} };
185+
const sdbus::Variant variant(value);
186+
187+
// This should produce something like:
188+
// VARIANT "a{ta(sd)}" {
189+
// ARRAY "{ta(sd)}" {
190+
// DICT_ENTRY "ta(sd)" {
191+
// UINT64 84578348354;
192+
// ARRAY "(sd)" {
193+
// STRUCT "sd" {
194+
// STRING "hello";
195+
// DOUBLE 3.14;
196+
// };
197+
// STRUCT "sd" {
198+
// STRING "world";
199+
// DOUBLE 3.14;
200+
// };
201+
// };
202+
// };
203+
// };
204+
// };
205+
const auto str = variant.dumpToString();
206+
207+
EXPECT_THAT(str, ::HasSubstr("VARIANT \"a{ta(sd)}\""));
208+
EXPECT_THAT(str, ::HasSubstr("hello"));
209+
EXPECT_THAT(str, ::HasSubstr("world"));
210+
}
211+
#endif // SDBUS_basu
212+
161213
TEST(AVariant, HasConceptuallyNonmutableGetMethodWhichCanBeCalledXTimes)
162214
{
163215
const std::string value{"I am a string"};

0 commit comments

Comments
 (0)