xref: /openbmc/bmcweb/features/redfish/include/utils/json_utils.hpp (revision c09966bdaac3b31e6a4b6adc75cc7481727f6c99)
177dd8813SKowalski, Kamil /*
277dd8813SKowalski, Kamil // Copyright (c) 2018 Intel Corporation
377dd8813SKowalski, Kamil //
477dd8813SKowalski, Kamil // Licensed under the Apache License, Version 2.0 (the "License");
577dd8813SKowalski, Kamil // you may not use this file except in compliance with the License.
677dd8813SKowalski, Kamil // You may obtain a copy of the License at
777dd8813SKowalski, Kamil //
877dd8813SKowalski, Kamil //      http://www.apache.org/licenses/LICENSE-2.0
977dd8813SKowalski, Kamil //
1077dd8813SKowalski, Kamil // Unless required by applicable law or agreed to in writing, software
1177dd8813SKowalski, Kamil // distributed under the License is distributed on an "AS IS" BASIS,
1277dd8813SKowalski, Kamil // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1377dd8813SKowalski, Kamil // See the License for the specific language governing permissions and
1477dd8813SKowalski, Kamil // limitations under the License.
1577dd8813SKowalski, Kamil */
1677dd8813SKowalski, Kamil #pragma once
179712f8acSEd Tanous 
18d5c80ad9SNan Zhou #include "error_messages.hpp"
198a7c4b47SNan Zhou #include "http_connection.hpp"
20d5c80ad9SNan Zhou #include "http_request.hpp"
21d5c80ad9SNan Zhou #include "http_response.hpp"
22185444b1SNan Zhou #include "human_sort.hpp"
23d5c80ad9SNan Zhou #include "logging.hpp"
24faf100f9SEd Tanous 
25faf100f9SEd Tanous #include <nlohmann/json.hpp>
260627a2c7SEd Tanous 
27185444b1SNan Zhou #include <algorithm>
28d5c80ad9SNan Zhou #include <array>
29d5c80ad9SNan Zhou #include <cmath>
30d5c80ad9SNan Zhou #include <cstddef>
31d5c80ad9SNan Zhou #include <cstdint>
32d5c80ad9SNan Zhou #include <limits>
33d5c80ad9SNan Zhou #include <map>
34d5c80ad9SNan Zhou #include <optional>
353544d2a7SEd Tanous #include <ranges>
36ea2e6eecSWilly Tu #include <span>
37d5c80ad9SNan Zhou #include <string>
38d5c80ad9SNan Zhou #include <string_view>
39d5c80ad9SNan Zhou #include <type_traits>
40d5c80ad9SNan Zhou #include <utility>
41d5c80ad9SNan Zhou #include <variant>
42d5c80ad9SNan Zhou #include <vector>
43d5c80ad9SNan Zhou 
44d5c80ad9SNan Zhou // IWYU pragma: no_include <stdint.h>
451e75e1ddSNan Zhou // IWYU pragma: no_forward_declare crow::Request
461214b7e7SGunnar Mills 
471abe55efSEd Tanous namespace redfish
481abe55efSEd Tanous {
491abe55efSEd Tanous 
501abe55efSEd Tanous namespace json_util
511abe55efSEd Tanous {
5277dd8813SKowalski, Kamil 
5377dd8813SKowalski, Kamil /**
5477dd8813SKowalski, Kamil  * @brief Processes request to extract JSON from its body. If it fails, adds
5577dd8813SKowalski, Kamil  *       MalformedJSON message to response and ends it.
5677dd8813SKowalski, Kamil  *
5777dd8813SKowalski, Kamil  * @param[io]  res       Response object
5877dd8813SKowalski, Kamil  * @param[in]  req       Request object
5977dd8813SKowalski, Kamil  * @param[out] reqJson   JSON object extracted from request's body
6077dd8813SKowalski, Kamil  *
6177dd8813SKowalski, Kamil  * @return true if JSON is valid, false when JSON is invalid and response has
6277dd8813SKowalski, Kamil  *         been filled with message and ended.
6377dd8813SKowalski, Kamil  */
6455c7b7a2SEd Tanous bool processJsonFromRequest(crow::Response& res, const crow::Request& req,
6577dd8813SKowalski, Kamil                             nlohmann::json& reqJson);
669712f8acSEd Tanous namespace details
679712f8acSEd Tanous {
68771cfa0fSJason M. Bills 
691214b7e7SGunnar Mills template <typename Type>
702c70f800SEd Tanous struct IsOptional : std::false_type
711214b7e7SGunnar Mills {};
729712f8acSEd Tanous 
73771cfa0fSJason M. Bills template <typename Type>
742c70f800SEd Tanous struct IsOptional<std::optional<Type>> : std::true_type
751214b7e7SGunnar Mills {};
769712f8acSEd Tanous 
77771cfa0fSJason M. Bills template <typename Type>
782c70f800SEd Tanous struct IsVector : std::false_type
791214b7e7SGunnar Mills {};
80b1556427SEd Tanous 
811214b7e7SGunnar Mills template <typename Type>
822c70f800SEd Tanous struct IsVector<std::vector<Type>> : std::true_type
831214b7e7SGunnar Mills {};
84b1556427SEd Tanous 
851214b7e7SGunnar Mills template <typename Type>
862c70f800SEd Tanous struct IsStdArray : std::false_type
871214b7e7SGunnar Mills {};
88318226c2SJames Feist 
89318226c2SJames Feist template <typename Type, std::size_t size>
902c70f800SEd Tanous struct IsStdArray<std::array<Type, size>> : std::true_type
911214b7e7SGunnar Mills {};
92318226c2SJames Feist 
938099c517SEd Tanous template <typename Type>
948099c517SEd Tanous struct IsVariant : std::false_type
958099c517SEd Tanous {};
968099c517SEd Tanous 
978099c517SEd Tanous template <typename... Types>
988099c517SEd Tanous struct IsVariant<std::variant<Types...>> : std::true_type
998099c517SEd Tanous {};
1008099c517SEd Tanous 
101471a5eb8SAppaRao Puli enum class UnpackErrorCode
102471a5eb8SAppaRao Puli {
103471a5eb8SAppaRao Puli     success,
104471a5eb8SAppaRao Puli     invalidType,
105471a5eb8SAppaRao Puli     outOfRange
106471a5eb8SAppaRao Puli };
107471a5eb8SAppaRao Puli 
108a6acbb31SJames Feist template <typename ToType, typename FromType>
109*c09966bdSEd Tanous bool checkRange(const FromType& from [[maybe_unused]],
110*c09966bdSEd Tanous                 std::string_view key [[maybe_unused]])
111a6acbb31SJames Feist {
112a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<ToType>)
113a6acbb31SJames Feist     {
114ee344e0fSEd Tanous         if (std::isnan(from))
115a6acbb31SJames Feist         {
11662598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was NAN", key);
117a6acbb31SJames Feist             return false;
118a6acbb31SJames Feist         }
119a6acbb31SJames Feist     }
120*c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::max() <
121*c09966bdSEd Tanous                   std::numeric_limits<FromType>::max())
122*c09966bdSEd Tanous     {
123*c09966bdSEd Tanous         if (from > std::numeric_limits<ToType>::max())
124*c09966bdSEd Tanous         {
125*c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was greater than max {}", key,
126*c09966bdSEd Tanous                              std::numeric_limits<FromType>::max());
127*c09966bdSEd Tanous             return false;
128*c09966bdSEd Tanous         }
129*c09966bdSEd Tanous     }
130*c09966bdSEd Tanous     if constexpr (std::numeric_limits<ToType>::lowest() >
131*c09966bdSEd Tanous                   std::numeric_limits<FromType>::lowest())
132*c09966bdSEd Tanous     {
133*c09966bdSEd Tanous         if (from < std::numeric_limits<ToType>::lowest())
134*c09966bdSEd Tanous         {
135*c09966bdSEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was less than min {}", key,
136*c09966bdSEd Tanous                              std::numeric_limits<FromType>::lowest());
137*c09966bdSEd Tanous             return false;
138*c09966bdSEd Tanous         }
139*c09966bdSEd Tanous     }
140a6acbb31SJames Feist 
141a6acbb31SJames Feist     return true;
142a6acbb31SJames Feist }
143a6acbb31SJames Feist 
144771cfa0fSJason M. Bills template <typename Type>
145471a5eb8SAppaRao Puli UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
1468099c517SEd Tanous                                          std::string_view key, Type& value);
1478099c517SEd Tanous 
1488099c517SEd Tanous template <std::size_t Index = 0, typename... Args>
1498099c517SEd Tanous UnpackErrorCode unpackValueVariant(nlohmann::json& j, std::string_view key,
1508099c517SEd Tanous                                    std::variant<Args...>& v)
1518099c517SEd Tanous {
1528099c517SEd Tanous     if constexpr (Index < std::variant_size_v<std::variant<Args...>>)
1538099c517SEd Tanous     {
1548099c517SEd Tanous         std::variant_alternative_t<Index, std::variant<Args...>> type;
1558099c517SEd Tanous         UnpackErrorCode unpack = unpackValueWithErrorCode(j, key, type);
1568099c517SEd Tanous         if (unpack == UnpackErrorCode::success)
1578099c517SEd Tanous         {
1588099c517SEd Tanous             v = std::move(type);
1598099c517SEd Tanous             return unpack;
1608099c517SEd Tanous         }
1618099c517SEd Tanous 
1628099c517SEd Tanous         return unpackValueVariant<Index + 1, Args...>(j, key, v);
1638099c517SEd Tanous     }
1648099c517SEd Tanous     return UnpackErrorCode::invalidType;
1658099c517SEd Tanous }
1668099c517SEd Tanous 
1678099c517SEd Tanous template <typename Type>
1688099c517SEd Tanous UnpackErrorCode unpackValueWithErrorCode(nlohmann::json& jsonValue,
169ea2e6eecSWilly Tu                                          std::string_view key, Type& value)
170771cfa0fSJason M. Bills {
171471a5eb8SAppaRao Puli     UnpackErrorCode ret = UnpackErrorCode::success;
17241352c24SSantosh Puranik 
173a6acbb31SJames Feist     if constexpr (std::is_floating_point_v<Type>)
174771cfa0fSJason M. Bills     {
175a6acbb31SJames Feist         double helper = 0;
176a6acbb31SJames Feist         double* jsonPtr = jsonValue.get_ptr<double*>();
177771cfa0fSJason M. Bills 
178771cfa0fSJason M. Bills         if (jsonPtr == nullptr)
179771cfa0fSJason M. Bills         {
180a6acbb31SJames Feist             int64_t* intPtr = jsonValue.get_ptr<int64_t*>();
181a6acbb31SJames Feist             if (intPtr != nullptr)
182771cfa0fSJason M. Bills             {
183a6acbb31SJames Feist                 helper = static_cast<double>(*intPtr);
184a6acbb31SJames Feist                 jsonPtr = &helper;
185771cfa0fSJason M. Bills             }
186a6acbb31SJames Feist         }
1875eb2bef2SAppaRao Puli         if (jsonPtr == nullptr)
1885eb2bef2SAppaRao Puli         {
189471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
1905eb2bef2SAppaRao Puli         }
191cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
192771cfa0fSJason M. Bills         {
193471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
194771cfa0fSJason M. Bills         }
195771cfa0fSJason M. Bills         value = static_cast<Type>(*jsonPtr);
196771cfa0fSJason M. Bills     }
197a6acbb31SJames Feist 
198a6acbb31SJames Feist     else if constexpr (std::is_signed_v<Type>)
199a6acbb31SJames Feist     {
200a6acbb31SJames Feist         int64_t* jsonPtr = jsonValue.get_ptr<int64_t*>();
201271584abSEd Tanous         if (jsonPtr == nullptr)
202271584abSEd Tanous         {
203471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
204271584abSEd Tanous         }
205cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
206a6acbb31SJames Feist         {
207471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
208a6acbb31SJames Feist         }
209a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
210a6acbb31SJames Feist     }
211a6acbb31SJames Feist 
2128102ddbaSAppaRao Puli     else if constexpr ((std::is_unsigned_v<Type>)&&(
2138102ddbaSAppaRao Puli                            !std::is_same_v<bool, Type>))
214a6acbb31SJames Feist     {
215a6acbb31SJames Feist         uint64_t* jsonPtr = jsonValue.get_ptr<uint64_t*>();
216271584abSEd Tanous         if (jsonPtr == nullptr)
217271584abSEd Tanous         {
218471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
219271584abSEd Tanous         }
220cb13a392SEd Tanous         if (!checkRange<Type>(*jsonPtr, key))
221a6acbb31SJames Feist         {
222471a5eb8SAppaRao Puli             return UnpackErrorCode::outOfRange;
223a6acbb31SJames Feist         }
224a6acbb31SJames Feist         value = static_cast<Type>(*jsonPtr);
225a6acbb31SJames Feist     }
226a6acbb31SJames Feist 
2270627a2c7SEd Tanous     else if constexpr (std::is_same_v<nlohmann::json, Type>)
2280627a2c7SEd Tanous     {
2290627a2c7SEd Tanous         value = std::move(jsonValue);
2300627a2c7SEd Tanous     }
2318099c517SEd Tanous     else if constexpr (std::is_same_v<std::nullptr_t, Type>)
2328099c517SEd Tanous     {
2338099c517SEd Tanous         if (!jsonValue.is_null())
2348099c517SEd Tanous         {
2358099c517SEd Tanous             return UnpackErrorCode::invalidType;
2368099c517SEd Tanous         }
2378099c517SEd Tanous     }
238471a5eb8SAppaRao Puli     else
239471a5eb8SAppaRao Puli     {
240471a5eb8SAppaRao Puli         using JsonType = std::add_const_t<std::add_pointer_t<Type>>;
241471a5eb8SAppaRao Puli         JsonType jsonPtr = jsonValue.get_ptr<JsonType>();
242471a5eb8SAppaRao Puli         if (jsonPtr == nullptr)
243471a5eb8SAppaRao Puli         {
24462598e31SEd Tanous             BMCWEB_LOG_DEBUG("Value for key {} was incorrect type: {}", key,
24562598e31SEd Tanous                              jsonValue.type_name());
246471a5eb8SAppaRao Puli             return UnpackErrorCode::invalidType;
247471a5eb8SAppaRao Puli         }
248471a5eb8SAppaRao Puli         value = std::move(*jsonPtr);
249471a5eb8SAppaRao Puli     }
250471a5eb8SAppaRao Puli     return ret;
251471a5eb8SAppaRao Puli }
252471a5eb8SAppaRao Puli 
253471a5eb8SAppaRao Puli template <typename Type>
254ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key,
255471a5eb8SAppaRao Puli                  crow::Response& res, Type& value)
256471a5eb8SAppaRao Puli {
257471a5eb8SAppaRao Puli     bool ret = true;
258471a5eb8SAppaRao Puli 
2592c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
260471a5eb8SAppaRao Puli     {
261471a5eb8SAppaRao Puli         value.emplace();
262471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, res,
263471a5eb8SAppaRao Puli                                                      *value) &&
264471a5eb8SAppaRao Puli               ret;
265471a5eb8SAppaRao Puli     }
2662c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
267318226c2SJames Feist     {
268318226c2SJames Feist         if (!jsonValue.is_array())
269318226c2SJames Feist         {
2702e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
27141352c24SSantosh Puranik             return false;
272318226c2SJames Feist         }
273318226c2SJames Feist         if (jsonValue.size() != value.size())
274318226c2SJames Feist         {
2752e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
27641352c24SSantosh Puranik             return false;
277318226c2SJames Feist         }
278318226c2SJames Feist         size_t index = 0;
279318226c2SJames Feist         for (const auto& val : jsonValue.items())
280318226c2SJames Feist         {
28141352c24SSantosh Puranik             ret = unpackValue<typename Type::value_type>(val.value(), key, res,
28241352c24SSantosh Puranik                                                          value[index++]) &&
28341352c24SSantosh Puranik                   ret;
284318226c2SJames Feist         }
285318226c2SJames Feist     }
2862c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
287b1556427SEd Tanous     {
288b1556427SEd Tanous         if (!jsonValue.is_array())
289b1556427SEd Tanous         {
2902e8c4bdaSEd Tanous             messages::propertyValueTypeError(res, res.jsonValue, key);
29141352c24SSantosh Puranik             return false;
292b1556427SEd Tanous         }
293b1556427SEd Tanous 
294b1556427SEd Tanous         for (const auto& val : jsonValue.items())
295b1556427SEd Tanous         {
296b1556427SEd Tanous             value.emplace_back();
29741352c24SSantosh Puranik             ret = unpackValue<typename Type::value_type>(val.value(), key, res,
29841352c24SSantosh Puranik                                                          value.back()) &&
29941352c24SSantosh Puranik                   ret;
300b1556427SEd Tanous         }
301b1556427SEd Tanous     }
3028099c517SEd Tanous     else if constexpr (IsVariant<Type>::value)
3038099c517SEd Tanous     {
3048099c517SEd Tanous         UnpackErrorCode ec = unpackValueVariant(jsonValue, key, value);
3058099c517SEd Tanous         if (ec != UnpackErrorCode::success)
3068099c517SEd Tanous         {
3078099c517SEd Tanous             if (ec == UnpackErrorCode::invalidType)
3088099c517SEd Tanous             {
3098099c517SEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
3108099c517SEd Tanous             }
3118099c517SEd Tanous             else if (ec == UnpackErrorCode::outOfRange)
3128099c517SEd Tanous             {
3138099c517SEd Tanous                 messages::propertyValueNotInList(res, jsonValue, key);
3148099c517SEd Tanous             }
3158099c517SEd Tanous             return false;
3168099c517SEd Tanous         }
3178099c517SEd Tanous     }
318771cfa0fSJason M. Bills     else
319771cfa0fSJason M. Bills     {
320471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
321471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
322771cfa0fSJason M. Bills         {
323471a5eb8SAppaRao Puli             if (ec == UnpackErrorCode::invalidType)
324471a5eb8SAppaRao Puli             {
3252e8c4bdaSEd Tanous                 messages::propertyValueTypeError(res, jsonValue, key);
326471a5eb8SAppaRao Puli             }
327471a5eb8SAppaRao Puli             else if (ec == UnpackErrorCode::outOfRange)
328471a5eb8SAppaRao Puli             {
329e2616cc5SEd Tanous                 messages::propertyValueNotInList(res, jsonValue, key);
330471a5eb8SAppaRao Puli             }
33141352c24SSantosh Puranik             return false;
332771cfa0fSJason M. Bills         }
333771cfa0fSJason M. Bills     }
334471a5eb8SAppaRao Puli 
335471a5eb8SAppaRao Puli     return ret;
336471a5eb8SAppaRao Puli }
337471a5eb8SAppaRao Puli 
338471a5eb8SAppaRao Puli template <typename Type>
339ea2e6eecSWilly Tu bool unpackValue(nlohmann::json& jsonValue, std::string_view key, Type& value)
340471a5eb8SAppaRao Puli {
341471a5eb8SAppaRao Puli     bool ret = true;
3422c70f800SEd Tanous     if constexpr (IsOptional<Type>::value)
343471a5eb8SAppaRao Puli     {
344471a5eb8SAppaRao Puli         value.emplace();
345471a5eb8SAppaRao Puli         ret = unpackValue<typename Type::value_type>(jsonValue, key, *value) &&
346471a5eb8SAppaRao Puli               ret;
347471a5eb8SAppaRao Puli     }
3482c70f800SEd Tanous     else if constexpr (IsStdArray<Type>::value)
349471a5eb8SAppaRao Puli     {
350471a5eb8SAppaRao Puli         if (!jsonValue.is_array())
351471a5eb8SAppaRao Puli         {
352471a5eb8SAppaRao Puli             return false;
353471a5eb8SAppaRao Puli         }
354471a5eb8SAppaRao Puli         if (jsonValue.size() != value.size())
355471a5eb8SAppaRao Puli         {
356471a5eb8SAppaRao Puli             return false;
357471a5eb8SAppaRao Puli         }
358471a5eb8SAppaRao Puli         size_t index = 0;
359471a5eb8SAppaRao Puli         for (const auto& val : jsonValue.items())
360471a5eb8SAppaRao Puli         {
361471a5eb8SAppaRao Puli             ret = unpackValue<typename Type::value_type>(val.value(), key,
362471a5eb8SAppaRao Puli                                                          value[index++]) &&
363471a5eb8SAppaRao Puli                   ret;
364471a5eb8SAppaRao Puli         }
365471a5eb8SAppaRao Puli     }
3662c70f800SEd Tanous     else if constexpr (IsVector<Type>::value)
367471a5eb8SAppaRao Puli     {
368471a5eb8SAppaRao Puli         if (!jsonValue.is_array())
369471a5eb8SAppaRao Puli         {
370471a5eb8SAppaRao Puli             return false;
371471a5eb8SAppaRao Puli         }
372471a5eb8SAppaRao Puli 
373471a5eb8SAppaRao Puli         for (const auto& val : jsonValue.items())
374471a5eb8SAppaRao Puli         {
375471a5eb8SAppaRao Puli             value.emplace_back();
376471a5eb8SAppaRao Puli             ret = unpackValue<typename Type::value_type>(val.value(), key,
377471a5eb8SAppaRao Puli                                                          value.back()) &&
378471a5eb8SAppaRao Puli                   ret;
379471a5eb8SAppaRao Puli         }
380471a5eb8SAppaRao Puli     }
381471a5eb8SAppaRao Puli     else
382471a5eb8SAppaRao Puli     {
383471a5eb8SAppaRao Puli         UnpackErrorCode ec = unpackValueWithErrorCode(jsonValue, key, value);
384471a5eb8SAppaRao Puli         if (ec != UnpackErrorCode::success)
385471a5eb8SAppaRao Puli         {
386471a5eb8SAppaRao Puli             return false;
387471a5eb8SAppaRao Puli         }
388471a5eb8SAppaRao Puli     }
389471a5eb8SAppaRao Puli 
39041352c24SSantosh Puranik     return ret;
391771cfa0fSJason M. Bills }
3929712f8acSEd Tanous } // namespace details
3939712f8acSEd Tanous 
394ea2e6eecSWilly Tu // clang-format off
395ea2e6eecSWilly Tu using UnpackVariant = std::variant<
396ea2e6eecSWilly Tu     uint8_t*,
397ea2e6eecSWilly Tu     uint16_t*,
398ea2e6eecSWilly Tu     int16_t*,
399ea2e6eecSWilly Tu     uint32_t*,
400ea2e6eecSWilly Tu     int32_t*,
401ea2e6eecSWilly Tu     uint64_t*,
402ea2e6eecSWilly Tu     int64_t*,
403ea2e6eecSWilly Tu     bool*,
404ea2e6eecSWilly Tu     double*,
405ea2e6eecSWilly Tu     std::string*,
406ea2e6eecSWilly Tu     nlohmann::json*,
407b6164cbeSEd Tanous     nlohmann::json::object_t*,
4088099c517SEd Tanous     std::variant<std::string, std::nullptr_t>*,
4098099c517SEd Tanous     std::variant<uint8_t, std::nullptr_t>*,
4108099c517SEd Tanous     std::variant<int16_t, std::nullptr_t>*,
4118099c517SEd Tanous     std::variant<uint16_t, std::nullptr_t>*,
4128099c517SEd Tanous     std::variant<int32_t, std::nullptr_t>*,
4138099c517SEd Tanous     std::variant<uint32_t, std::nullptr_t>*,
4148099c517SEd Tanous     std::variant<int64_t, std::nullptr_t>*,
4158099c517SEd Tanous     std::variant<uint64_t, std::nullptr_t>*,
4168099c517SEd Tanous     std::variant<double, std::nullptr_t>*,
4178099c517SEd Tanous     std::variant<bool, std::nullptr_t>*,
418ea2e6eecSWilly Tu     std::vector<uint8_t>*,
419ea2e6eecSWilly Tu     std::vector<uint16_t>*,
420ea2e6eecSWilly Tu     std::vector<int16_t>*,
421ea2e6eecSWilly Tu     std::vector<uint32_t>*,
422ea2e6eecSWilly Tu     std::vector<int32_t>*,
423ea2e6eecSWilly Tu     std::vector<uint64_t>*,
424ea2e6eecSWilly Tu     std::vector<int64_t>*,
425ea2e6eecSWilly Tu     //std::vector<bool>*,
426ea2e6eecSWilly Tu     std::vector<double>*,
427ea2e6eecSWilly Tu     std::vector<std::string>*,
428ea2e6eecSWilly Tu     std::vector<nlohmann::json>*,
429b6164cbeSEd Tanous     std::vector<nlohmann::json::object_t>*,
430ea2e6eecSWilly Tu     std::optional<uint8_t>*,
431ea2e6eecSWilly Tu     std::optional<uint16_t>*,
432ea2e6eecSWilly Tu     std::optional<int16_t>*,
433ea2e6eecSWilly Tu     std::optional<uint32_t>*,
434ea2e6eecSWilly Tu     std::optional<int32_t>*,
435ea2e6eecSWilly Tu     std::optional<uint64_t>*,
436ea2e6eecSWilly Tu     std::optional<int64_t>*,
437ea2e6eecSWilly Tu     std::optional<bool>*,
438ea2e6eecSWilly Tu     std::optional<double>*,
439ea2e6eecSWilly Tu     std::optional<std::string>*,
440ea2e6eecSWilly Tu     std::optional<nlohmann::json>*,
441b6164cbeSEd Tanous     std::optional<nlohmann::json::object_t>*,
442ea2e6eecSWilly Tu     std::optional<std::vector<uint8_t>>*,
443ea2e6eecSWilly Tu     std::optional<std::vector<uint16_t>>*,
444ea2e6eecSWilly Tu     std::optional<std::vector<int16_t>>*,
445ea2e6eecSWilly Tu     std::optional<std::vector<uint32_t>>*,
446ea2e6eecSWilly Tu     std::optional<std::vector<int32_t>>*,
447ea2e6eecSWilly Tu     std::optional<std::vector<uint64_t>>*,
448ea2e6eecSWilly Tu     std::optional<std::vector<int64_t>>*,
449ea2e6eecSWilly Tu     //std::optional<std::vector<bool>>*,
450ea2e6eecSWilly Tu     std::optional<std::vector<double>>*,
451ea2e6eecSWilly Tu     std::optional<std::vector<std::string>>*,
452b6164cbeSEd Tanous     std::optional<std::vector<nlohmann::json>>*,
4538099c517SEd Tanous     std::optional<std::vector<nlohmann::json::object_t>>*,
4548099c517SEd Tanous     std::optional<std::variant<std::string, std::nullptr_t>>*,
4558099c517SEd Tanous     std::optional<std::variant<uint8_t, std::nullptr_t>>*,
4568099c517SEd Tanous     std::optional<std::variant<int16_t, std::nullptr_t>>*,
4578099c517SEd Tanous     std::optional<std::variant<uint16_t, std::nullptr_t>>*,
4588099c517SEd Tanous     std::optional<std::variant<int32_t, std::nullptr_t>>*,
4598099c517SEd Tanous     std::optional<std::variant<uint32_t, std::nullptr_t>>*,
4608099c517SEd Tanous     std::optional<std::variant<int64_t, std::nullptr_t>>*,
4618099c517SEd Tanous     std::optional<std::variant<uint64_t, std::nullptr_t>>*,
4628099c517SEd Tanous     std::optional<std::variant<double, std::nullptr_t>>*,
4638099c517SEd Tanous     std::optional<std::variant<bool, std::nullptr_t>>*,
4648099c517SEd Tanous     std::optional<std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>*,
4658099c517SEd Tanous     std::optional<std::variant<nlohmann::json::object_t, std::nullptr_t>>*
466ea2e6eecSWilly Tu >;
467ea2e6eecSWilly Tu // clang-format on
468ea2e6eecSWilly Tu 
469ea2e6eecSWilly Tu struct PerUnpack
470ea2e6eecSWilly Tu {
471ea2e6eecSWilly Tu     std::string_view key;
472ea2e6eecSWilly Tu     UnpackVariant value;
473ea2e6eecSWilly Tu     bool complete = false;
474ea2e6eecSWilly Tu };
475ea2e6eecSWilly Tu 
476ea2e6eecSWilly Tu inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
477b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack);
478b6164cbeSEd Tanous 
479b6164cbeSEd Tanous inline bool readJsonHelperObject(nlohmann::json::object_t& obj,
480b6164cbeSEd Tanous                                  crow::Response& res,
481ea2e6eecSWilly Tu                                  std::span<PerUnpack> toUnpack)
4829712f8acSEd Tanous {
48341352c24SSantosh Puranik     bool result = true;
484b6164cbeSEd Tanous     for (auto& item : obj)
4859712f8acSEd Tanous     {
486ea2e6eecSWilly Tu         size_t unpackIndex = 0;
487ea2e6eecSWilly Tu         for (; unpackIndex < toUnpack.size(); unpackIndex++)
488ea2e6eecSWilly Tu         {
489ea2e6eecSWilly Tu             PerUnpack& unpackSpec = toUnpack[unpackIndex];
490ea2e6eecSWilly Tu             std::string_view key = unpackSpec.key;
491ea2e6eecSWilly Tu             size_t keysplitIndex = key.find('/');
492ea2e6eecSWilly Tu             std::string_view leftover;
493ea2e6eecSWilly Tu             if (keysplitIndex != std::string_view::npos)
494ea2e6eecSWilly Tu             {
495ea2e6eecSWilly Tu                 leftover = key.substr(keysplitIndex + 1);
496ea2e6eecSWilly Tu                 key = key.substr(0, keysplitIndex);
497ea2e6eecSWilly Tu             }
498ea2e6eecSWilly Tu 
499d91415c4SEd Tanous             if (key != item.first || unpackSpec.complete)
500ea2e6eecSWilly Tu             {
501ea2e6eecSWilly Tu                 continue;
502ea2e6eecSWilly Tu             }
503ea2e6eecSWilly Tu 
504ea2e6eecSWilly Tu             // Sublevel key
505ea2e6eecSWilly Tu             if (!leftover.empty())
506ea2e6eecSWilly Tu             {
507ea2e6eecSWilly Tu                 // Include the slash in the key so we can compare later
508ea2e6eecSWilly Tu                 key = unpackSpec.key.substr(0, keysplitIndex + 1);
509ea2e6eecSWilly Tu                 nlohmann::json j;
510d91415c4SEd Tanous                 result = details::unpackValue<nlohmann::json>(item.second, key,
511ea2e6eecSWilly Tu                                                               res, j) &&
51241352c24SSantosh Puranik                          result;
51355f79e6fSEd Tanous                 if (!result)
514ea2e6eecSWilly Tu                 {
515ea2e6eecSWilly Tu                     return result;
5169712f8acSEd Tanous                 }
5179712f8acSEd Tanous 
518ea2e6eecSWilly Tu                 std::vector<PerUnpack> nextLevel;
519ea2e6eecSWilly Tu                 for (PerUnpack& p : toUnpack)
520ea2e6eecSWilly Tu                 {
521ea2e6eecSWilly Tu                     if (!p.key.starts_with(key))
522ea2e6eecSWilly Tu                     {
523ea2e6eecSWilly Tu                         continue;
524ea2e6eecSWilly Tu                     }
525ea2e6eecSWilly Tu                     std::string_view thisLeftover = p.key.substr(key.size());
526ea2e6eecSWilly Tu                     nextLevel.push_back({thisLeftover, p.value, false});
527ea2e6eecSWilly Tu                     p.complete = true;
5289712f8acSEd Tanous                 }
52977dd8813SKowalski, Kamil 
530ea2e6eecSWilly Tu                 result = readJsonHelper(j, res, nextLevel) && result;
531ea2e6eecSWilly Tu                 break;
532ea2e6eecSWilly Tu             }
533ea2e6eecSWilly Tu 
534002d39b4SEd Tanous             result = std::visit(
535ea2e6eecSWilly Tu                          [&item, &unpackSpec, &res](auto&& val) {
536ea2e6eecSWilly Tu                 using ContainedT =
537ea2e6eecSWilly Tu                     std::remove_pointer_t<std::decay_t<decltype(val)>>;
538ea2e6eecSWilly Tu                 return details::unpackValue<ContainedT>(
539d91415c4SEd Tanous                     item.second, unpackSpec.key, res, *val);
540ea2e6eecSWilly Tu             },
541ea2e6eecSWilly Tu                          unpackSpec.value) &&
542ea2e6eecSWilly Tu                      result;
543ea2e6eecSWilly Tu 
544ea2e6eecSWilly Tu             unpackSpec.complete = true;
545ea2e6eecSWilly Tu             break;
546ea2e6eecSWilly Tu         }
547ea2e6eecSWilly Tu 
548ea2e6eecSWilly Tu         if (unpackIndex == toUnpack.size())
549ea2e6eecSWilly Tu         {
550d91415c4SEd Tanous             messages::propertyUnknown(res, item.first);
551ea2e6eecSWilly Tu             result = false;
552ea2e6eecSWilly Tu         }
553ea2e6eecSWilly Tu     }
554ea2e6eecSWilly Tu 
555ea2e6eecSWilly Tu     for (PerUnpack& perUnpack : toUnpack)
556ea2e6eecSWilly Tu     {
55755f79e6fSEd Tanous         if (!perUnpack.complete)
558ea2e6eecSWilly Tu         {
559ea2e6eecSWilly Tu             bool isOptional = std::visit(
560ea2e6eecSWilly Tu                 [](auto&& val) {
561ea2e6eecSWilly Tu                 using ContainedType =
562ea2e6eecSWilly Tu                     std::remove_pointer_t<std::decay_t<decltype(val)>>;
563ea2e6eecSWilly Tu                 return details::IsOptional<ContainedType>::value;
564ea2e6eecSWilly Tu             },
565ea2e6eecSWilly Tu                 perUnpack.value);
566ea2e6eecSWilly Tu             if (isOptional)
567ea2e6eecSWilly Tu             {
568ea2e6eecSWilly Tu                 continue;
569ea2e6eecSWilly Tu             }
570ea2e6eecSWilly Tu             messages::propertyMissing(res, perUnpack.key);
571ea2e6eecSWilly Tu             result = false;
572ea2e6eecSWilly Tu         }
573ea2e6eecSWilly Tu     }
574ea2e6eecSWilly Tu     return result;
575ea2e6eecSWilly Tu }
576ea2e6eecSWilly Tu 
577b6164cbeSEd Tanous inline bool readJsonHelper(nlohmann::json& jsonRequest, crow::Response& res,
578b6164cbeSEd Tanous                            std::span<PerUnpack> toUnpack)
579b6164cbeSEd Tanous {
580b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
581b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
582b6164cbeSEd Tanous     if (obj == nullptr)
583b6164cbeSEd Tanous     {
584b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
585b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
586b6164cbeSEd Tanous         return false;
587b6164cbeSEd Tanous     }
588b6164cbeSEd Tanous     return readJsonHelperObject(*obj, res, toUnpack);
589b6164cbeSEd Tanous }
590b6164cbeSEd Tanous 
59189492a15SPatrick Williams inline void packVariant(std::span<PerUnpack> /*toPack*/) {}
592ea2e6eecSWilly Tu 
593ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
594ea2e6eecSWilly Tu void packVariant(std::span<PerUnpack> toPack, std::string_view key,
595ea2e6eecSWilly Tu                  FirstType& first, UnpackTypes&&... in)
596ea2e6eecSWilly Tu {
597ea2e6eecSWilly Tu     if (toPack.empty())
598ea2e6eecSWilly Tu     {
599ea2e6eecSWilly Tu         return;
600ea2e6eecSWilly Tu     }
601ea2e6eecSWilly Tu     toPack[0].key = key;
602ea2e6eecSWilly Tu     toPack[0].value = &first;
603ea2e6eecSWilly Tu     // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
604ea2e6eecSWilly Tu     packVariant(toPack.subspan(1), std::forward<UnpackTypes&&>(in)...);
605ea2e6eecSWilly Tu }
606ea2e6eecSWilly Tu 
607ea2e6eecSWilly Tu template <typename FirstType, typename... UnpackTypes>
608b6164cbeSEd Tanous bool readJsonObject(nlohmann::json::object_t& jsonRequest, crow::Response& res,
609b6164cbeSEd Tanous                     std::string_view key, FirstType&& first,
610b6164cbeSEd Tanous                     UnpackTypes&&... in)
611ea2e6eecSWilly Tu {
612ea2e6eecSWilly Tu     const std::size_t n = sizeof...(UnpackTypes) + 2;
613ea2e6eecSWilly Tu     std::array<PerUnpack, n / 2> toUnpack2;
614ea2e6eecSWilly Tu     packVariant(toUnpack2, key, first, std::forward<UnpackTypes&&>(in)...);
615b6164cbeSEd Tanous     return readJsonHelperObject(jsonRequest, res, toUnpack2);
616ea2e6eecSWilly Tu }
617ea2e6eecSWilly Tu 
618b6164cbeSEd Tanous template <typename FirstType, typename... UnpackTypes>
619b6164cbeSEd Tanous bool readJson(nlohmann::json& jsonRequest, crow::Response& res,
620b6164cbeSEd Tanous               std::string_view key, FirstType&& first, UnpackTypes&&... in)
621b6164cbeSEd Tanous {
622b6164cbeSEd Tanous     nlohmann::json::object_t* obj =
623b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
624b6164cbeSEd Tanous     if (obj == nullptr)
625b6164cbeSEd Tanous     {
626b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is not an object");
627b6164cbeSEd Tanous         messages::unrecognizedRequestBody(res);
628b6164cbeSEd Tanous         return false;
629b6164cbeSEd Tanous     }
6305be2b14aSEd Tanous     return readJsonObject(*obj, res, key, std::forward<FirstType>(first),
6315be2b14aSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
632b6164cbeSEd Tanous }
633b6164cbeSEd Tanous 
634b6164cbeSEd Tanous inline std::optional<nlohmann::json::object_t>
635ea2e6eecSWilly Tu     readJsonPatchHelper(const crow::Request& req, crow::Response& res)
6360627a2c7SEd Tanous {
6370627a2c7SEd Tanous     nlohmann::json jsonRequest;
6380627a2c7SEd Tanous     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
6390627a2c7SEd Tanous     {
64062598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
641ea2e6eecSWilly Tu         return std::nullopt;
6420627a2c7SEd Tanous     }
643357bb8f8SEd Tanous     nlohmann::json::object_t* object =
644357bb8f8SEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
645357bb8f8SEd Tanous     if (object == nullptr || object->empty())
64615ed6780SWilly Tu     {
64762598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
64815ed6780SWilly Tu         messages::emptyJSON(res);
649ea2e6eecSWilly Tu         return std::nullopt;
650ea2e6eecSWilly Tu     }
651357bb8f8SEd Tanous     std::erase_if(*object,
652357bb8f8SEd Tanous                   [](const std::pair<std::string, nlohmann::json>& item) {
653357bb8f8SEd Tanous         return item.first.starts_with("@odata.");
654357bb8f8SEd Tanous     });
655357bb8f8SEd Tanous     if (object->empty())
656357bb8f8SEd Tanous     {
657357bb8f8SEd Tanous         //  If the update request only contains OData annotations, the service
658357bb8f8SEd Tanous         //  should return the HTTP 400 Bad Request status code with the
659357bb8f8SEd Tanous         //  NoOperation message from the Base Message Registry, ...
660357bb8f8SEd Tanous         messages::noOperation(res);
661357bb8f8SEd Tanous         return std::nullopt;
662357bb8f8SEd Tanous     }
663357bb8f8SEd Tanous 
664b6164cbeSEd Tanous     return {std::move(*object)};
665ea2e6eecSWilly Tu }
666ea2e6eecSWilly Tu 
667ea2e6eecSWilly Tu template <typename... UnpackTypes>
668ea2e6eecSWilly Tu bool readJsonPatch(const crow::Request& req, crow::Response& res,
669ea2e6eecSWilly Tu                    std::string_view key, UnpackTypes&&... in)
670ea2e6eecSWilly Tu {
67176b038f2SEd Tanous     std::optional<nlohmann::json::object_t> jsonRequest =
67276b038f2SEd Tanous         readJsonPatchHelper(req, res);
673e01d0c36SEd Tanous     if (!jsonRequest)
674ea2e6eecSWilly Tu     {
67515ed6780SWilly Tu         return false;
67615ed6780SWilly Tu     }
67776b038f2SEd Tanous     if (jsonRequest->empty())
678b6164cbeSEd Tanous     {
679b6164cbeSEd Tanous         messages::emptyJSON(res);
680b6164cbeSEd Tanous         return false;
681b6164cbeSEd Tanous     }
68215ed6780SWilly Tu 
68376b038f2SEd Tanous     return readJsonObject(*jsonRequest, res, key,
684b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
68515ed6780SWilly Tu }
68615ed6780SWilly Tu 
68715ed6780SWilly Tu template <typename... UnpackTypes>
68815ed6780SWilly Tu bool readJsonAction(const crow::Request& req, crow::Response& res,
689ea2e6eecSWilly Tu                     const char* key, UnpackTypes&&... in)
69015ed6780SWilly Tu {
69115ed6780SWilly Tu     nlohmann::json jsonRequest;
69215ed6780SWilly Tu     if (!json_util::processJsonFromRequest(res, req, jsonRequest))
69315ed6780SWilly Tu     {
69462598e31SEd Tanous         BMCWEB_LOG_DEBUG("Json value not readable");
69515ed6780SWilly Tu         return false;
69615ed6780SWilly Tu     }
697b6164cbeSEd Tanous     nlohmann::json::object_t* object =
698b6164cbeSEd Tanous         jsonRequest.get_ptr<nlohmann::json::object_t*>();
699b6164cbeSEd Tanous     if (object == nullptr)
700b6164cbeSEd Tanous     {
701b6164cbeSEd Tanous         BMCWEB_LOG_DEBUG("Json value is empty");
702b6164cbeSEd Tanous         messages::emptyJSON(res);
703b6164cbeSEd Tanous         return false;
704b6164cbeSEd Tanous     }
705b6164cbeSEd Tanous     return readJsonObject(*object, res, key,
706b6164cbeSEd Tanous                           std::forward<UnpackTypes&&>(in)...);
7070627a2c7SEd Tanous }
708185444b1SNan Zhou 
709185444b1SNan Zhou // Determines if two json objects are less, based on the presence of the
710185444b1SNan Zhou // @odata.id key
711185444b1SNan Zhou inline int odataObjectCmp(const nlohmann::json& a, const nlohmann::json& b)
712185444b1SNan Zhou {
713185444b1SNan Zhou     using object_t = nlohmann::json::object_t;
714185444b1SNan Zhou     const object_t* aObj = a.get_ptr<const object_t*>();
715185444b1SNan Zhou     const object_t* bObj = b.get_ptr<const object_t*>();
716185444b1SNan Zhou 
717185444b1SNan Zhou     if (aObj == nullptr)
718185444b1SNan Zhou     {
719185444b1SNan Zhou         if (bObj == nullptr)
720185444b1SNan Zhou         {
721185444b1SNan Zhou             return 0;
722185444b1SNan Zhou         }
723185444b1SNan Zhou         return -1;
724185444b1SNan Zhou     }
725185444b1SNan Zhou     if (bObj == nullptr)
726185444b1SNan Zhou     {
727185444b1SNan Zhou         return 1;
728185444b1SNan Zhou     }
729185444b1SNan Zhou     object_t::const_iterator aIt = aObj->find("@odata.id");
730185444b1SNan Zhou     object_t::const_iterator bIt = bObj->find("@odata.id");
731185444b1SNan Zhou     // If either object doesn't have the key, they get "sorted" to the end.
732185444b1SNan Zhou     if (aIt == aObj->end())
733185444b1SNan Zhou     {
734185444b1SNan Zhou         if (bIt == bObj->end())
735185444b1SNan Zhou         {
736185444b1SNan Zhou             return 0;
737185444b1SNan Zhou         }
738185444b1SNan Zhou         return -1;
739185444b1SNan Zhou     }
740185444b1SNan Zhou     if (bIt == bObj->end())
741185444b1SNan Zhou     {
742185444b1SNan Zhou         return 1;
743185444b1SNan Zhou     }
744185444b1SNan Zhou     const nlohmann::json::string_t* nameA =
745185444b1SNan Zhou         aIt->second.get_ptr<const std::string*>();
746185444b1SNan Zhou     const nlohmann::json::string_t* nameB =
747185444b1SNan Zhou         bIt->second.get_ptr<const std::string*>();
748185444b1SNan Zhou     // If either object doesn't have a string as the key, they get "sorted" to
749185444b1SNan Zhou     // the end.
750185444b1SNan Zhou     if (nameA == nullptr)
751185444b1SNan Zhou     {
752185444b1SNan Zhou         if (nameB == nullptr)
753185444b1SNan Zhou         {
754185444b1SNan Zhou             return 0;
755185444b1SNan Zhou         }
756185444b1SNan Zhou         return -1;
757185444b1SNan Zhou     }
758185444b1SNan Zhou     if (nameB == nullptr)
759185444b1SNan Zhou     {
760185444b1SNan Zhou         return 1;
761185444b1SNan Zhou     }
762185444b1SNan Zhou     boost::urls::url_view aUrl(*nameA);
763185444b1SNan Zhou     boost::urls::url_view bUrl(*nameB);
764185444b1SNan Zhou     auto segmentsAIt = aUrl.segments().begin();
765185444b1SNan Zhou     auto segmentsBIt = bUrl.segments().begin();
766185444b1SNan Zhou 
767185444b1SNan Zhou     while (true)
768185444b1SNan Zhou     {
769185444b1SNan Zhou         if (segmentsAIt == aUrl.segments().end())
770185444b1SNan Zhou         {
771185444b1SNan Zhou             if (segmentsBIt == bUrl.segments().end())
772185444b1SNan Zhou             {
773185444b1SNan Zhou                 return 0;
774185444b1SNan Zhou             }
775185444b1SNan Zhou             return -1;
776185444b1SNan Zhou         }
777185444b1SNan Zhou         if (segmentsBIt == bUrl.segments().end())
778185444b1SNan Zhou         {
779185444b1SNan Zhou             return 1;
780185444b1SNan Zhou         }
781185444b1SNan Zhou         int res = alphanumComp(*segmentsAIt, *segmentsBIt);
782185444b1SNan Zhou         if (res != 0)
783185444b1SNan Zhou         {
784185444b1SNan Zhou             return res;
785185444b1SNan Zhou         }
786185444b1SNan Zhou 
787185444b1SNan Zhou         segmentsAIt++;
788185444b1SNan Zhou         segmentsBIt++;
789185444b1SNan Zhou     }
790185444b1SNan Zhou };
791185444b1SNan Zhou 
792185444b1SNan Zhou struct ODataObjectLess
793185444b1SNan Zhou {
794185444b1SNan Zhou     bool operator()(const nlohmann::json& left,
795185444b1SNan Zhou                     const nlohmann::json& right) const
796185444b1SNan Zhou     {
797185444b1SNan Zhou         return odataObjectCmp(left, right) < 0;
798185444b1SNan Zhou     }
799185444b1SNan Zhou };
800185444b1SNan Zhou 
801185444b1SNan Zhou // Sort the JSON array by |element[key]|.
802185444b1SNan Zhou // Elements without |key| or type of |element[key]| is not string are smaller
803185444b1SNan Zhou // those whose |element[key]| is string.
804185444b1SNan Zhou inline void sortJsonArrayByOData(nlohmann::json::array_t& array)
805185444b1SNan Zhou {
8063544d2a7SEd Tanous     std::ranges::sort(array, ODataObjectLess());
807185444b1SNan Zhou }
808185444b1SNan Zhou 
8098a7c4b47SNan Zhou // Returns the estimated size of the JSON value
8108a7c4b47SNan Zhou // The implementation walks through every key and every value, accumulates the
8118a7c4b47SNan Zhou //  total size of keys and values.
8128a7c4b47SNan Zhou // Ideally, we should use a custom allocator that nlohmann JSON supports.
8138a7c4b47SNan Zhou 
8148a7c4b47SNan Zhou // Assumption made:
8158a7c4b47SNan Zhou //  1. number: 8 characters
8168a7c4b47SNan Zhou //  2. boolean: 5 characters (False)
8178a7c4b47SNan Zhou //  3. string: len(str) + 2 characters (quote)
8188a7c4b47SNan Zhou //  4. bytes: len(bytes) characters
8198a7c4b47SNan Zhou //  5. null: 4 characters (null)
8208a7c4b47SNan Zhou uint64_t getEstimatedJsonSize(const nlohmann::json& root);
8218a7c4b47SNan Zhou 
82277dd8813SKowalski, Kamil } // namespace json_util
82377dd8813SKowalski, Kamil } // namespace redfish
824