userver: userver/dynamic_config/value.hpp Source File
Loading...
Searching...
No Matches
value.hpp
1#pragma once
2
3#include <optional>
4#include <string>
5#include <unordered_set>
6
7#include <userver/formats/json/value.hpp>
8#include <userver/formats/parse/common_containers.hpp>
9#include <userver/formats/serialize/common_containers.hpp>
10#include <userver/utils/impl/transparent_hash.hpp>
11#include <userver/utils/internal_tag_fwd.hpp>
12
13USERVER_NAMESPACE_BEGIN
14
15namespace dynamic_config {
16
17class DocsMap final {
18 public:
19 /* Returns config item or throws an exception if key is missing */
20 formats::json::Value Get(std::string_view name) const;
21
22 bool Has(std::string_view name) const;
23 void Set(std::string name, formats::json::Value);
24 void Parse(const std::string& json, bool empty_ok);
25 size_t Size() const;
26
27 void MergeOrAssign(DocsMap&& source);
28 void MergeMissing(const DocsMap& source);
29
30 std::unordered_set<std::string> GetNames() const;
31 std::string AsJsonString() const;
32 bool AreContentsEqual(const DocsMap& other) const;
33
34 /// @cond
35 // For internal use only
36 // Set of configs expected to be used is automatically updated when
37 // configs are retrieved with 'Get' method.
38 void SetConfigsExpectedToBeUsed(
39 utils::impl::TransparentSet<std::string> configs, utils::InternalTag);
40
41 const utils::impl::TransparentSet<std::string>& GetConfigsExpectedToBeUsed(
42 utils::InternalTag) const;
43 /// @endcond
44
45 private:
46 utils::impl::TransparentMap<std::string, formats::json::Value> docs_;
47 mutable utils::impl::TransparentSet<std::string> configs_to_be_used_;
48};
49
50template <typename T>
51class Value final {
52 public:
53 Value(const std::string& name, const DocsMap& docs_map)
54 : value_(docs_map.Get(name).As<T>()) {}
55
56 operator const T&() const { return value_; }
57
58 const T& Get() const { return value_; }
59 const T* operator->() const { return &value_; }
60
61 private:
62 T value_;
63};
64
65extern const std::string kValueDictDefaultName;
66
67namespace impl {
68
69[[noreturn]] void ThrowNoValueException(std::string_view dict_name,
70 std::string_view key);
71
72} // namespace impl
73
74template <typename ValueType>
75class ValueDict final {
76 public:
77 using DictType = utils::impl::TransparentMap<std::string, ValueType>;
78 using const_iterator = typename DictType::const_iterator;
79 using iterator = const_iterator;
80 using value_type = typename DictType::value_type;
81 using key_type = std::string;
82 using mapped_type = ValueType;
83
84 ValueDict() = default;
85
86 ValueDict(std::initializer_list<value_type> contents) : dict_(contents) {}
87
88 ValueDict(DictType dict) : dict_(std::move(dict)) {}
89
90 ValueDict(std::string name, DictType dict)
91 : name_(std::move(name)), dict_(std::move(dict)) {}
92
93 // Deprecated
94 ValueDict(std::string name, const DocsMap& docs_map)
95 : name_(std::move(name)),
96 dict_(docs_map.Get(name_).template As<DictType>()) {}
97
98 bool HasDefaultValue() const { return HasValue(kValueDictDefaultName); }
99
100 bool HasValue(std::string_view key) const {
101 return utils::impl::FindTransparent(dict_, key) != dict_.end();
102 }
103
104 const ValueType& GetDefaultValue() const {
105 const auto it = dict_.find(kValueDictDefaultName);
106 if (it == dict_.end()) {
107 impl::ThrowNoValueException(name_, kValueDictDefaultName);
108 }
109 return it->second;
110 }
111
112 const ValueType& operator[](std::string_view key) const {
113 auto it = utils::impl::FindTransparent(dict_, key);
114 if (it == dict_.end()) {
115 it = dict_.find(kValueDictDefaultName);
116 if (it == dict_.end()) {
117 impl::ThrowNoValueException(name_, key);
118 }
119 }
120 return it->second;
121 }
122
123 template <typename StringType>
124 const ValueType& operator[](const std::optional<StringType>& key) const {
125 if (key) return (*this)[*key];
126 return GetDefaultValue();
127 }
128
129 const ValueType& Get(std::string_view key) const { return (*this)[key]; }
130
131 template <typename StringType>
132 const ValueType& Get(const std::optional<StringType>& key) const {
133 return (*this)[key];
134 }
135
136 std::optional<ValueType> GetOptional(std::string_view key) const {
137 auto it = utils::impl::FindTransparent(dict_, key);
138 if (it == dict_.end()) {
139 it = dict_.find(kValueDictDefaultName);
140 if (it == dict_.end()) return std::nullopt;
141 }
142
143 return it->second;
144 }
145
146 /// Sets the default value.
147 /// The function is primarily there for testing purposes - ValueDict is
148 /// normally obtained by parsing the config.
149 void SetDefault(ValueType value) {
150 Set(kValueDictDefaultName, std::move(value));
151 }
152
153 /// Sets a mapping. key == dynamic_config::kValueDictDefaultName is allowed.
154 /// The function is primarily there for testing purposes - ValueDict is
155 /// normally obtained by parsing the config.
156 template <typename StringType>
157 void Set(StringType&& key, ValueType value) {
158 utils::impl::TransparentInsertOrAssign(dict_, std::forward<StringType>(key),
159 std::move(value));
160 }
161
162 auto begin() const { return dict_.begin(); }
163
164 auto end() const { return dict_.end(); }
165
166 const std::string& GetName() const { return name_; }
167
168 bool operator==(const ValueDict& r) const { return dict_ == r.dict_; }
169
170 bool operator!=(const ValueDict& r) const { return !(*this == r); }
171
172 private:
173 std::string name_;
174 DictType dict_;
175};
176
177template <typename T>
178ValueDict<T> Parse(const formats::json::Value& value,
179 formats::parse::To<ValueDict<T>>) {
180 return ValueDict<T>{value.GetPath(),
181 value.As<typename ValueDict<T>::DictType>()};
182}
183
184} // namespace dynamic_config
185
186USERVER_NAMESPACE_END