userver: userver/formats/common/utils.hpp Source File
Loading...
Searching...
No Matches
utils.hpp
Go to the documentation of this file.
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,
28 std::size_t path_size) {
29 UINVARIANT(path_size != 0,
30 "attempt to get a ValueBuilder element on an empty path");
31 if (path_size == 1) {
32 return parent[std::move(path[0])];
33 }
34 std::optional<ValueBuilder> current_element;
35
36 current_element.emplace(TransferTag(), parent[std::move(path[0])]);
37 for (std::size_t i = 1; i < path_size - 1; i++) {
38 current_element.emplace(TransferTag(),
39 (*current_element)[std::move(path[i])]);
40 }
41 return (*current_element)[std::move(path[path_size - 1])];
42}
43
44} // namespace impl
45
46/// @brief Get the `Value` at `path` in `parent`.
47/// @note For empty `path` this function returns `parent`.
48/// @throws TypeMismatchException if there is a non-object node in the middle of
49/// `path`
50template <typename Value>
52 Value parent, const std::vector<std::string>& path) {
53 auto current_value = std::move(parent);
54 for (const auto& current_key : path) {
55 current_value = current_value[current_key];
56 }
57 return current_value;
58}
59
60/// @brief Get the `ValueBuilder` at `path` in `parent`.
61/// @note `path` must not be empty.
62/// @throws TypeMismatchException if there is a non-object node in the middle of
63/// `path`
64template <typename ValueBuilder>
67 return impl::GetAtPath(parent, std::move(path), path.size());
68}
69
70/// @brief Set the `new_value` along the `path` in the `parent`.
71/// @note If `path` is empty it sets `new_value` to `parent`.
72/// @throws TypeMismatchException if there is a non-object node in the middle of
73/// `path`
74template <typename Value>
75void SetAtPath(typename Value::Builder& parent, std::vector<std::string>&& path,
76 Value new_value) {
77 if (path.empty()) {
78 parent = std::move(new_value);
79 } else {
80 GetAtPath(parent, std::move(path)) = std::move(new_value);
81 }
82}
83
84/// @brief Remove the element along the `path` in the `parent`.
85/// @note If `path` is empty or there is no node at `path`, this function does
86/// nothing.
87/// @throws TypeMismatchException if there is a non-object node in the middle of
88/// `path`
89template <typename ValueBuilder>
90void RemoveAtPath(ValueBuilder& parent, std::vector<std::string>&& path) {
91 if (path.empty()) {
92 parent = ValueBuilder();
93 } else if (path.size() == 1) {
94 parent.Remove(path[0]);
95 } else {
96 auto& key = path[path.size() - 1];
97 impl::GetAtPath(parent, std::move(path), path.size() - 1).Remove(key);
98 }
99}
100
101/// @brief Split `path` to `vector<std::string>` by dots.
102/// @note If `path` has a double dot or a dot at the beginning or end, the
103/// result will contain an empty string.
104/// @note Returns an empty `vector` if `path` is empty
106
107} // namespace formats::common
108
109USERVER_NAMESPACE_END