1 #pragma once
2 
3 #include <sdbusplus/exception.hpp>
4 #include <sdbusplus/utility/type_traits.hpp>
5 
6 #include <algorithm>
7 #include <bitset>
8 #include <optional>
9 #include <stdexcept>
10 #include <string>
11 #include <string_view>
12 #include <variant>
13 #include <vector>
14 
15 namespace sdbusplus
16 {
17 
18 namespace details
19 {
20 
21 template <typename VariantType>
findProperty(const std::vector<std::pair<std::string,VariantType>> & container,const std::string & key)22 inline auto findProperty(
23     const std::vector<std::pair<std::string, VariantType>>& container,
24     const std::string& key) noexcept
25 {
26     return std::find_if(
27         container.begin(), container.end(),
28         [&key](const auto& keyValue) { return keyValue.first == key; });
29 }
30 
31 template <typename OnErrorCallback, typename VariantType, typename ValueType>
readProperty(const OnErrorCallback & onErrorCallback,const std::vector<std::pair<std::string,VariantType>> & container,const std::string & expectedKey,ValueType & outValue)32 inline bool readProperty(
33     const OnErrorCallback& onErrorCallback,
34     const std::vector<std::pair<std::string, VariantType>>& container,
35     const std::string& expectedKey,
36     ValueType&
37         outValue) noexcept(noexcept(onErrorCallback(sdbusplus::
38                                                         UnpackErrorReason{},
39                                                     std::string{})))
40 {
41     auto it = findProperty(container, expectedKey);
42 
43     if (it != container.end())
44     {
45         if constexpr (std::is_pointer_v<ValueType>)
46         {
47             if (const auto* value = std::get_if<
48                     std::remove_cv_t<std::remove_pointer_t<ValueType>>>(
49                     &it->second))
50             {
51                 outValue = value;
52             }
53             else
54             {
55                 onErrorCallback(UnpackErrorReason::wrongType, expectedKey);
56                 return false;
57             }
58         }
59         else if constexpr (utility::is_optional_v<ValueType>)
60         {
61             using InnerType = typename ValueType::value_type;
62             static_assert(!std::is_pointer_v<InnerType>,
63                           "std::optional<T*> is not supported");
64             if (const auto value = std::get_if<InnerType>(&it->second))
65 
66             {
67                 outValue = *value;
68             }
69             else
70             {
71                 onErrorCallback(UnpackErrorReason::wrongType, expectedKey);
72                 return false;
73             }
74         }
75         else
76         {
77             if (const auto value = std::get_if<ValueType>(&it->second))
78             {
79                 outValue = *value;
80             }
81             else
82             {
83                 onErrorCallback(UnpackErrorReason::wrongType, expectedKey);
84                 return false;
85             }
86         }
87     }
88     else if constexpr (!utility::is_optional_v<ValueType> &&
89                        !std::is_pointer_v<ValueType>)
90     {
91         onErrorCallback(UnpackErrorReason::missingProperty, expectedKey);
92         return false;
93     }
94 
95     return true;
96 }
97 
98 template <typename OnErrorCallback, typename VariantType, typename ValueType,
99           typename... Args>
readProperties(OnErrorCallback && onErrorCallback,const std::vector<std::pair<std::string,VariantType>> & container,const std::string & expectedKey,ValueType & outValue,Args &&...args)100 inline bool readProperties(
101     OnErrorCallback&& onErrorCallback,
102     const std::vector<std::pair<std::string, VariantType>>& container,
103     const std::string& expectedKey, ValueType& outValue,
104     Args&&... args) noexcept(noexcept(onErrorCallback(sdbusplus::
105                                                           UnpackErrorReason{},
106                                                       std::string{})))
107 {
108     if (!readProperty(onErrorCallback, container, expectedKey, outValue))
109     {
110         return false;
111     }
112 
113     if constexpr (sizeof...(Args) > 0)
114     {
115         return readProperties(std::forward<OnErrorCallback>(onErrorCallback),
116                               container, std::forward<Args>(args)...);
117     }
118 
119     return true;
120 }
121 
122 template <typename OnErrorCallback, typename VariantType, typename... Args>
unpackPropertiesCommon(OnErrorCallback && onErrorCallback,const std::vector<std::pair<std::string,VariantType>> & input,Args &&...args)123 inline auto unpackPropertiesCommon(
124     OnErrorCallback&& onErrorCallback,
125     const std::vector<std::pair<std::string, VariantType>>& input,
126     Args&&... args) noexcept(noexcept(onErrorCallback(sdbusplus::
127                                                           UnpackErrorReason{},
128                                                       std::string{})))
129 {
130     static_assert(
131         sizeof...(Args) % 2 == 0,
132         "Expected number of arguments to be even, but got odd number instead");
133 
134     return details::readProperties(
135         std::forward<OnErrorCallback>(onErrorCallback), input,
136         std::forward<Args>(args)...);
137 }
138 
139 } // namespace details
140 
141 template <typename VariantType, typename... Args>
unpackProperties(const std::vector<std::pair<std::string,VariantType>> & input,Args &&...args)142 inline void unpackProperties(
143     const std::vector<std::pair<std::string, VariantType>>& input,
144     Args&&... args)
145 {
146     details::unpackPropertiesCommon(
147         [](const UnpackErrorReason reason, const std::string& property) {
148         throw exception::UnpackPropertyError(property, reason);
149     },
150         input, std::forward<Args>(args)...);
151 }
152 
153 template <typename OnErrorCallback, typename VariantType, typename... Args>
unpackPropertiesNoThrow(OnErrorCallback && onErrorCallback,const std::vector<std::pair<std::string,VariantType>> & input,Args &&...args)154 inline bool unpackPropertiesNoThrow(
155     OnErrorCallback&& onErrorCallback,
156     const std::vector<std::pair<std::string, VariantType>>& input,
157     Args&&... args) noexcept
158 {
159     return details::unpackPropertiesCommon(
160         std::forward<OnErrorCallback>(onErrorCallback), input,
161         std::forward<Args>(args)...);
162 }
163 
164 } // namespace sdbusplus
165