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 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>
48std::enable_if_t<common::kIsFormatValue<Value>, Value> GetAtPath(Value parent, const std::vector<std::string>& path) {
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>
61std::enable_if_t<!common::kIsFormatValue<ValueBuilder>, ValueBuilder> GetAtPath(
62 ValueBuilder& parent,
63 std::vector<std::string>&& path
64) {
65 return impl::GetAtPath(parent, std::move(path), path.size());
66}
67
68/// @brief Set the `new_value` along the `path` in the `parent`.
69/// @note If `path` is empty it sets `new_value` to `parent`.
70/// @throws TypeMismatchException if there is a non-object node in the middle of
71/// `path`
72template <typename Value>
73void SetAtPath(typename Value::Builder& parent, std::vector<std::string>&& path, Value new_value) {
74 if (path.empty()) {
75 parent = std::move(new_value);
76 } else {
77 GetAtPath(parent, std::move(path)) = std::move(new_value);
78 }
79}
80
81/// @brief Remove the element along the `path` in the `parent`.
82/// @note If `path` is empty or there is no node at `path`, this function does
83/// nothing.
84/// @throws TypeMismatchException if there is a non-object node in the middle of
85/// `path`
86template <typename ValueBuilder>
87void RemoveAtPath(ValueBuilder& parent, std::vector<std::string>&& path) {
88 if (path.empty()) {
89 parent = ValueBuilder();
90 } else if (path.size() == 1) {
91 parent.Remove(path[0]);
92 } else {
93 auto& key = path[path.size() - 1];
94 impl::GetAtPath(parent, std::move(path), path.size() - 1).Remove(key);
95 }
96}
97
98/// @brief Split `path` to `vector<std::string>` by dots.
99/// @note If `path` has a double dot or a dot at the beginning or end, the
100/// result will contain an empty string.
101/// @note Returns an empty `vector` if `path` is empty
102std::vector<std::string> SplitPathString(std::string_view path);
103
104} // namespace formats::common
105
106USERVER_NAMESPACE_END