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 
14 namespace sdbusplus
15 {
16 namespace detail
17 {
18 
19 template <typename Variant, typename ValueType>
20 bool getIf(Variant&& variant, ValueType& outValue)
21 {
22     if (auto value = std::get_if<ValueType>(&variant))
23     {
24         outValue = std::move(*value);
25         return true;
26     }
27 
28     return false;
29 }
30 
31 template <typename Container>
32 auto findProperty(Container&& container, const std::string& key)
33 {
34     if constexpr (utility::has_member_find_v<Container>)
35     {
36         return container.find(key);
37     }
38     else
39     {
40         return std::find_if(
41             std::begin(container), std::end(container),
42             [&key](const auto& keyValue) { return keyValue.first == key; });
43     }
44 }
45 
46 template <typename Container>
47 bool containsProperty(Container&& container, const std::string& key)
48 {
49     if constexpr (utility::has_member_contains_v<Container>)
50     {
51         return container.contains(key);
52     }
53     else
54     {
55         return findProperty(std::forward<Container>(container), key) !=
56                std::end(container);
57     }
58 }
59 
60 template <size_t Index, typename Container, size_t N, typename ValueType,
61           typename... Args>
62 void readProperties(Container&& container, std::bitset<N>& assigned,
63                     const std::string& expectedKey, ValueType& outValue,
64                     Args&&... args)
65 {
66     static_assert(Index < N);
67 
68     auto it = findProperty(std::forward<Container>(container), expectedKey);
69 
70     if (it != std::end(container))
71     {
72         if (getIf(it->second, outValue))
73         {
74             assigned.set(Index);
75         }
76     }
77 
78     if constexpr (sizeof...(Args) > 0)
79     {
80         readProperties<Index + 1>(std::forward<Container>(container), assigned,
81                                   std::forward<Args>(args)...);
82     }
83 }
84 
85 template <size_t Index, size_t N, typename ValueType, typename... Args>
86 std::string findMissingProperty(std::bitset<N>& assigned,
87                                 const std::string& key, ValueType&,
88                                 Args&&... args)
89 {
90     static_assert(Index < N);
91 
92     if (!assigned.test(Index))
93     {
94         return key;
95     }
96 
97     if constexpr (sizeof...(Args) > 0)
98     {
99         return findMissingProperty<Index + 1>(assigned,
100                                               std::forward<Args>(args)...);
101     }
102 
103     return {};
104 }
105 
106 } // namespace detail
107 
108 template <typename Container, typename... Args>
109 void unpackProperties(Container&& input, Args&&... args)
110 {
111     static_assert(sizeof...(Args) % 2 == 0);
112 
113     auto assigned = std::bitset<sizeof...(Args) / 2>();
114 
115     detail::readProperties<0>(input, assigned, std::forward<Args>(args)...);
116 
117     if (!assigned.all())
118     {
119         std::string missingProperty = detail::findMissingProperty<0>(
120             assigned, std::forward<Args>(args)...);
121 
122         if (detail::containsProperty(std::forward<Container>(input),
123                                      missingProperty))
124         {
125             throw exception::UnpackPropertyError(
126                 missingProperty,
127                 exception::UnpackPropertyError::reasonTypeNotMatched);
128         }
129         else
130         {
131             throw exception::UnpackPropertyError(
132                 missingProperty,
133                 exception::UnpackPropertyError::reasonMissingProperty);
134         }
135     }
136 }
137 
138 } // namespace sdbusplus
139