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