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) noexcept
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) noexcept
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) noexcept
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) noexcept
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) noexcept
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 template <bool ReturnBadProperty, typename Container, typename... Args>
107 auto unpackPropertiesCommon(Container&& input,
108                             Args&&... args) noexcept(ReturnBadProperty)
109 {
110     static_assert(sizeof...(Args) % 2 == 0);
111 
112     auto assigned = std::bitset<sizeof...(Args) / 2>();
113 
114     detail::readProperties<0>(input, assigned, std::forward<Args>(args)...);
115 
116     if (!assigned.all())
117     {
118         auto missingProperty = detail::findMissingProperty<0>(
119             assigned, std::forward<Args>(args)...);
120 
121         if constexpr (ReturnBadProperty)
122         {
123             return std::optional{missingProperty};
124         }
125         else
126         {
127             if (detail::containsProperty(std::forward<Container>(input),
128                                          missingProperty))
129             {
130                 throw exception::UnpackPropertyError(
131                     missingProperty,
132                     exception::UnpackPropertyError::reasonTypeNotMatched);
133             }
134             else
135             {
136                 throw exception::UnpackPropertyError(
137                     missingProperty,
138                     exception::UnpackPropertyError::reasonMissingProperty);
139             }
140         }
141     }
142     return std::conditional_t<ReturnBadProperty, std::optional<std::string>,
143                               void>();
144 }
145 
146 } // namespace detail
147 
148 template <typename Container, typename... Args>
149 void unpackProperties(Container&& input, Args&&... args)
150 {
151     detail::unpackPropertiesCommon<false, Container, Args...>(
152         std::forward<Container>(input), std::forward<Args>(args)...);
153 }
154 
155 template <typename Container, typename... Args>
156 std::optional<std::string> unpackPropertiesNoThrow(Container&& input,
157                                                    Args&&... args) noexcept
158 {
159     return detail::unpackPropertiesCommon<true, Container, Args...>(
160         std::forward<Container>(input), std::forward<Args>(args)...);
161 }
162 
163 } // namespace sdbusplus
164