userver: userver/formats/common/utils.hpp Source File
Loading...
Searching...
No Matches
utils.hpp
1#pragma once
2
3/// @file include/userver/formats/common/utils.hpp
4/// @brief formats::common::GetAtPath and formats::common::RemoveAtPath utils
5/// for `Value` and `ValueBuilder`
6/// @ingroup userver_universal
7
8#include <cstddef>
9#include <optional>
10#include <string>
11#include <string_view>
12#include <utility>
13#include <vector>
14
15#include <userver/formats/common/meta.hpp>
16#include <userver/formats/common/transfer_tag.hpp>
17#include <userver/formats/common/type.hpp>
18#include <userver/utils/assert.hpp>
19
20USERVER_NAMESPACE_BEGIN
21
22namespace formats::common {
23
24namespace impl {
25
26template <typename ValueBuilder>
27ValueBuilder GetAtPath(ValueBuilder& parent, std::vector<std::string>&& path, std::size_t path_size) {
28 UINVARIANT(path_size != 0, "attempt to get a ValueBuilder element on an empty path");
29 if (path_size == 1) {
30 return parent[std::move(path[0])];
31 }
32 std::optional<ValueBuilder> current_element;
33
34 current_element.emplace(TransferTag(), parent[std::move(path[0])]);
35 for (std::size_t i = 1; i < path_size - 1; i++) {
36 current_element.emplace(TransferTag(), (*current_element)[std::move(path[i])]);
37 }
38 return (*current_element)[std::move(path[path_size - 1])];
39}
40
41} // namespace impl
42
43/// @brief Get the `Value` at `path` in `parent`.
44/// @note For empty `path` this function returns `parent`.
45/// @throws TypeMismatchException if there is a non-object node in the middle of
46/// `path`
47template <typename Value>
49 auto current_value = std::move(parent);
50 for (const auto& current_key : path) {
51 current_value = current_value[current_key];
52 }
53 return current_value;
54}
55
56/// @brief Get the `ValueBuilder` at `path` in `parent`.
57/// @note `path` must not be empty.
58/// @throws TypeMismatchException if there is a non-object node in the middle of
59/// `path`
60template <typename ValueBuilder>
63 return impl::GetAtPath(parent, std::move(path), path.size());
64}
65
66/// @brief Set the `new_value` along the `path` in the `parent`.
67/// @note If `path` is empty it sets `new_value` to `parent`.
68/// @throws TypeMismatchException if there is a non-object node in the middle of
69/// `path`
70template <typename Value>
71void SetAtPath(typename Value::Builder& parent, std::vector<std::string>&& path, Value new_value) {
72 if (path.empty()) {
73 parent = std::move(new_value);
74 } else {
75 GetAtPath(parent, std::move(path)) = std::move(new_value);
76 }
77}
78
79/// @brief Remove the element along the `path` in the `parent`.
80/// @note If `path` is empty or there is no node at `path`, this function does
81/// nothing.
82/// @throws TypeMismatchException if there is a non-object node in the middle of
83/// `path`
84template <typename ValueBuilder>
85void RemoveAtPath(ValueBuilder& parent, std::vector<std::string>&& path) {
86 if (path.empty()) {
87 parent = ValueBuilder();
88 } else if (path.size() == 1) {
89 parent.Remove(path[0]);
90 } else {
91 auto& key = path[path.size() - 1];
92 impl::GetAtPath(parent, std::move(path), path.size() - 1).Remove(key);
93 }
94}
95
96/// @brief Split `path` to `vector<std::string>` by dots.
97/// @note If `path` has a double dot or a dot at the beginning or end, the
98/// result will contain an empty string.
99/// @note Returns an empty `vector` if `path` is empty
101
102} // namespace formats::common
103
104USERVER_NAMESPACE_END