userver: userver/formats/common/utils.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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