109b88f26SKrzysztof Grobelny #pragma once
209b88f26SKrzysztof Grobelny 
309b88f26SKrzysztof Grobelny #include <sdbusplus/exception.hpp>
409b88f26SKrzysztof Grobelny #include <sdbusplus/utility/type_traits.hpp>
509b88f26SKrzysztof Grobelny 
609b88f26SKrzysztof Grobelny #include <algorithm>
709b88f26SKrzysztof Grobelny #include <bitset>
809b88f26SKrzysztof Grobelny #include <optional>
909b88f26SKrzysztof Grobelny #include <stdexcept>
1009b88f26SKrzysztof Grobelny #include <string>
1109b88f26SKrzysztof Grobelny #include <string_view>
1209b88f26SKrzysztof Grobelny #include <variant>
1309b88f26SKrzysztof Grobelny 
1409b88f26SKrzysztof Grobelny namespace sdbusplus
1509b88f26SKrzysztof Grobelny {
1609b88f26SKrzysztof Grobelny namespace detail
1709b88f26SKrzysztof Grobelny {
1809b88f26SKrzysztof Grobelny 
1909b88f26SKrzysztof Grobelny template <typename Variant, typename ValueType>
20*6d83cf53SSzymon Dompke bool getIf(Variant&& variant, ValueType& outValue) noexcept
2109b88f26SKrzysztof Grobelny {
2209b88f26SKrzysztof Grobelny     if (auto value = std::get_if<ValueType>(&variant))
2309b88f26SKrzysztof Grobelny     {
2409b88f26SKrzysztof Grobelny         outValue = std::move(*value);
2509b88f26SKrzysztof Grobelny         return true;
2609b88f26SKrzysztof Grobelny     }
2709b88f26SKrzysztof Grobelny 
2809b88f26SKrzysztof Grobelny     return false;
2909b88f26SKrzysztof Grobelny }
3009b88f26SKrzysztof Grobelny 
3109b88f26SKrzysztof Grobelny template <typename Container>
32*6d83cf53SSzymon Dompke auto findProperty(Container&& container, const std::string& key) noexcept
3309b88f26SKrzysztof Grobelny {
3409b88f26SKrzysztof Grobelny     if constexpr (utility::has_member_find_v<Container>)
3509b88f26SKrzysztof Grobelny     {
3609b88f26SKrzysztof Grobelny         return container.find(key);
3709b88f26SKrzysztof Grobelny     }
3809b88f26SKrzysztof Grobelny     else
3909b88f26SKrzysztof Grobelny     {
4009b88f26SKrzysztof Grobelny         return std::find_if(
4109b88f26SKrzysztof Grobelny             std::begin(container), std::end(container),
4209b88f26SKrzysztof Grobelny             [&key](const auto& keyValue) { return keyValue.first == key; });
4309b88f26SKrzysztof Grobelny     }
4409b88f26SKrzysztof Grobelny }
4509b88f26SKrzysztof Grobelny 
4609b88f26SKrzysztof Grobelny template <typename Container>
47*6d83cf53SSzymon Dompke bool containsProperty(Container&& container, const std::string& key) noexcept
4809b88f26SKrzysztof Grobelny {
4909b88f26SKrzysztof Grobelny     if constexpr (utility::has_member_contains_v<Container>)
5009b88f26SKrzysztof Grobelny     {
5109b88f26SKrzysztof Grobelny         return container.contains(key);
5209b88f26SKrzysztof Grobelny     }
5309b88f26SKrzysztof Grobelny     else
5409b88f26SKrzysztof Grobelny     {
5509b88f26SKrzysztof Grobelny         return findProperty(std::forward<Container>(container), key) !=
5609b88f26SKrzysztof Grobelny                std::end(container);
5709b88f26SKrzysztof Grobelny     }
5809b88f26SKrzysztof Grobelny }
5909b88f26SKrzysztof Grobelny 
6009b88f26SKrzysztof Grobelny template <size_t Index, typename Container, size_t N, typename ValueType,
6109b88f26SKrzysztof Grobelny           typename... Args>
6209b88f26SKrzysztof Grobelny void readProperties(Container&& container, std::bitset<N>& assigned,
6309b88f26SKrzysztof Grobelny                     const std::string& expectedKey, ValueType& outValue,
64*6d83cf53SSzymon Dompke                     Args&&... args) noexcept
6509b88f26SKrzysztof Grobelny {
6609b88f26SKrzysztof Grobelny     static_assert(Index < N);
6709b88f26SKrzysztof Grobelny 
6809b88f26SKrzysztof Grobelny     auto it = findProperty(std::forward<Container>(container), expectedKey);
6909b88f26SKrzysztof Grobelny 
7009b88f26SKrzysztof Grobelny     if (it != std::end(container))
7109b88f26SKrzysztof Grobelny     {
7209b88f26SKrzysztof Grobelny         if (getIf(it->second, outValue))
7309b88f26SKrzysztof Grobelny         {
7409b88f26SKrzysztof Grobelny             assigned.set(Index);
7509b88f26SKrzysztof Grobelny         }
7609b88f26SKrzysztof Grobelny     }
7709b88f26SKrzysztof Grobelny 
7809b88f26SKrzysztof Grobelny     if constexpr (sizeof...(Args) > 0)
7909b88f26SKrzysztof Grobelny     {
8009b88f26SKrzysztof Grobelny         readProperties<Index + 1>(std::forward<Container>(container), assigned,
8109b88f26SKrzysztof Grobelny                                   std::forward<Args>(args)...);
8209b88f26SKrzysztof Grobelny     }
8309b88f26SKrzysztof Grobelny }
8409b88f26SKrzysztof Grobelny 
8509b88f26SKrzysztof Grobelny template <size_t Index, size_t N, typename ValueType, typename... Args>
8609b88f26SKrzysztof Grobelny std::string findMissingProperty(std::bitset<N>& assigned,
8709b88f26SKrzysztof Grobelny                                 const std::string& key, ValueType&,
88*6d83cf53SSzymon Dompke                                 Args&&... args) noexcept
8909b88f26SKrzysztof Grobelny {
9009b88f26SKrzysztof Grobelny     static_assert(Index < N);
9109b88f26SKrzysztof Grobelny 
9209b88f26SKrzysztof Grobelny     if (!assigned.test(Index))
9309b88f26SKrzysztof Grobelny     {
9409b88f26SKrzysztof Grobelny         return key;
9509b88f26SKrzysztof Grobelny     }
9609b88f26SKrzysztof Grobelny 
9709b88f26SKrzysztof Grobelny     if constexpr (sizeof...(Args) > 0)
9809b88f26SKrzysztof Grobelny     {
9909b88f26SKrzysztof Grobelny         return findMissingProperty<Index + 1>(assigned,
10009b88f26SKrzysztof Grobelny                                               std::forward<Args>(args)...);
10109b88f26SKrzysztof Grobelny     }
10209b88f26SKrzysztof Grobelny 
10309b88f26SKrzysztof Grobelny     return {};
10409b88f26SKrzysztof Grobelny }
10509b88f26SKrzysztof Grobelny 
106*6d83cf53SSzymon Dompke template <bool ReturnBadProperty, typename Container, typename... Args>
107*6d83cf53SSzymon Dompke auto unpackPropertiesCommon(Container&& input,
108*6d83cf53SSzymon Dompke                             Args&&... args) noexcept(ReturnBadProperty)
10909b88f26SKrzysztof Grobelny {
11009b88f26SKrzysztof Grobelny     static_assert(sizeof...(Args) % 2 == 0);
11109b88f26SKrzysztof Grobelny 
11209b88f26SKrzysztof Grobelny     auto assigned = std::bitset<sizeof...(Args) / 2>();
11309b88f26SKrzysztof Grobelny 
11409b88f26SKrzysztof Grobelny     detail::readProperties<0>(input, assigned, std::forward<Args>(args)...);
11509b88f26SKrzysztof Grobelny 
11609b88f26SKrzysztof Grobelny     if (!assigned.all())
11709b88f26SKrzysztof Grobelny     {
118*6d83cf53SSzymon Dompke         auto missingProperty = detail::findMissingProperty<0>(
11909b88f26SKrzysztof Grobelny             assigned, std::forward<Args>(args)...);
12009b88f26SKrzysztof Grobelny 
121*6d83cf53SSzymon Dompke         if constexpr (ReturnBadProperty)
122*6d83cf53SSzymon Dompke         {
123*6d83cf53SSzymon Dompke             return std::optional{missingProperty};
124*6d83cf53SSzymon Dompke         }
125*6d83cf53SSzymon Dompke         else
126*6d83cf53SSzymon Dompke         {
12709b88f26SKrzysztof Grobelny             if (detail::containsProperty(std::forward<Container>(input),
12809b88f26SKrzysztof Grobelny                                          missingProperty))
12909b88f26SKrzysztof Grobelny             {
13009b88f26SKrzysztof Grobelny                 throw exception::UnpackPropertyError(
13109b88f26SKrzysztof Grobelny                     missingProperty,
13209b88f26SKrzysztof Grobelny                     exception::UnpackPropertyError::reasonTypeNotMatched);
13309b88f26SKrzysztof Grobelny             }
13409b88f26SKrzysztof Grobelny             else
13509b88f26SKrzysztof Grobelny             {
13609b88f26SKrzysztof Grobelny                 throw exception::UnpackPropertyError(
13709b88f26SKrzysztof Grobelny                     missingProperty,
13809b88f26SKrzysztof Grobelny                     exception::UnpackPropertyError::reasonMissingProperty);
13909b88f26SKrzysztof Grobelny             }
14009b88f26SKrzysztof Grobelny         }
14109b88f26SKrzysztof Grobelny     }
142*6d83cf53SSzymon Dompke     return std::conditional_t<ReturnBadProperty, std::optional<std::string>,
143*6d83cf53SSzymon Dompke                               void>();
144*6d83cf53SSzymon Dompke }
145*6d83cf53SSzymon Dompke 
146*6d83cf53SSzymon Dompke } // namespace detail
147*6d83cf53SSzymon Dompke 
148*6d83cf53SSzymon Dompke template <typename Container, typename... Args>
149*6d83cf53SSzymon Dompke void unpackProperties(Container&& input, Args&&... args)
150*6d83cf53SSzymon Dompke {
151*6d83cf53SSzymon Dompke     detail::unpackPropertiesCommon<false, Container, Args...>(
152*6d83cf53SSzymon Dompke         std::forward<Container>(input), std::forward<Args>(args)...);
153*6d83cf53SSzymon Dompke }
154*6d83cf53SSzymon Dompke 
155*6d83cf53SSzymon Dompke template <typename Container, typename... Args>
156*6d83cf53SSzymon Dompke std::optional<std::string> unpackPropertiesNoThrow(Container&& input,
157*6d83cf53SSzymon Dompke                                                    Args&&... args) noexcept
158*6d83cf53SSzymon Dompke {
159*6d83cf53SSzymon Dompke     return detail::unpackPropertiesCommon<true, Container, Args...>(
160*6d83cf53SSzymon Dompke         std::forward<Container>(input), std::forward<Args>(args)...);
161*6d83cf53SSzymon Dompke }
16209b88f26SKrzysztof Grobelny 
16309b88f26SKrzysztof Grobelny } // namespace sdbusplus
164