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