userver: userver/dynamic_config/snapshot.hpp Source File
Loading...
Searching...
No Matches
snapshot.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/dynamic_config/snapshot.hpp
4/// @brief @copybrief dynamic_config::Snapshot
5
6#include <cstdint>
7#include <string>
8#include <type_traits>
9
10#include <userver/compiler/impl/lifetime.hpp>
11#include <userver/dynamic_config/impl/snapshot.hpp>
12#include <userver/dynamic_config/impl/to_json.hpp>
13#include <userver/formats/json_fwd.hpp>
14#include <userver/utils/fast_pimpl.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace dynamic_config {
19
20/// A strong typedef for usage in dynamic_config::Key constructors.
21struct DefaultAsJsonString final {
22 constexpr explicit DefaultAsJsonString(std::string_view json_string);
23
24 std::string_view json_string;
25};
26
27/// A config name-value pair for usage in dynamic_config::Key constructors.
28struct ConfigDefault final {
29 template <typename T>
30 ConfigDefault(std::string_view name, const T& value);
31
32 ConfigDefault(std::string_view name, DefaultAsJsonString default_json);
33
34 std::string_view name;
35 std::string default_json;
36};
37
38/// A tag type for usage in dynamic_config::Key constructors.
39struct ConstantConfig final {
40 constexpr explicit ConstantConfig() = default;
41};
42
43/// @brief A config key is a unique identifier for a config variable
44/// @snippet core/src/dynamic_config/config_test.cpp key bool
45template <typename Variable>
46class Key final {
47public:
48 /// The type of the parsed config variable.
49 using VariableType = Variable;
50
51 using JsonParser = Variable (*)(const formats::json::Value&);
52 using DocsMapParser = Variable (*)(const DocsMap&);
53
54 /// @brief The constructor for a trivial `VariableType`, e.g. `bool`, integer,
55 /// `double`, `string`. The default is passed by value.
56 ///
57 /// Usage example:
58 /// @snippet core/src/dynamic_config/config_test.cpp key bool
59 Key(std::string_view name, const VariableType& default_value);
60
61 /// @brief The constructor for a non-trivial `VariableType`. The default is
62 /// passed as a JSON string.
63 ///
64 /// Uses formats::json::Value `Parse` customization point function to parse
65 /// `VariableType`.
66 ///
67 /// Usage example:
68 /// @snippet core/src/dynamic_config/config_test.cpp struct config cpp
69 Key(std::string_view name, DefaultAsJsonString default_json);
70
71 /// @brief The constructor that provides a special parser from JSON.
72 /// @warning Prefer the constructors above whenever possible.
73 /// @details Can be used when generic `Parse` is not applicable. Sometimes
74 /// used to add validation, e.g. minimum, maximum, string pattern, etc.
75 Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json);
76
77 /// @brief The constructor that parses multiple JSON config items
78 /// into a single C++ object.
79 /// @warning Prefer to use a separate `Key` per JSON config item and use the
80 /// constructors above whenever possible.
81 template <std::size_t N>
82 Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N]);
83
84 /// Creates a config that always has the same value.
85 Key(ConstantConfig, VariableType value);
86
87 /// @cond
88 Key(impl::InternalTag, std::string_view name);
89
90 Key(impl::InternalTag, DocsMapParser parser);
91 /// @endcond
92
93 Key(const Key&) noexcept = delete;
94 Key& operator=(const Key&) noexcept = delete;
95
96 /// @returns the name of the config variable, as passed at the construction.
97 std::string_view GetName() const noexcept;
98
99 /// Parses the config. Useful only in some very niche scenarios. The config
100 /// value should be typically be retrieved from dynamic_config::Snapshot,
101 /// which is obtained from components::DynamicConfig in production or from
102 /// dynamic_config::StorageMock in unit tests.
103 VariableType Parse(const DocsMap& docs_map) const;
104
105private:
106 friend struct impl::ConfigIdGetter;
107
108 const impl::ConfigId id_;
109};
110
111// clang-format off
112
113/// @brief The shared snapshot of
114/// @ref scripts/docs/en/userver/dynamic_config.md "dynamic configs". Cheap to
115/// copy, even cheaper to move. Thread safe, not updated with new dynamic
116/// config values in background (it's a snapshot!).
117///
118/// When a config update comes in via new `DocsMap`, configs of all
119/// the registered types are constructed and stored in `Config`. After that
120/// the `DocsMap` is dropped.
121///
122/// Config types are automatically registered if they are used
123/// somewhere in the program.
124///
125/// ## Usage example:
126/// @snippet components/component_sample_test.cpp Sample user component runtime config source
127
128// clang-format on
129class Snapshot final {
130public:
131 Snapshot(const Snapshot&);
132 Snapshot& operator=(const Snapshot&);
133
134 Snapshot(Snapshot&&) noexcept;
135 Snapshot& operator=(Snapshot&&) noexcept;
136
137 ~Snapshot();
138
139 /// Used to access individual configs in the type-safe config map
140 template <typename VariableType>
141 const VariableType& operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND;
142
143 /// Used to access individual configs in the type-safe config map
144 template <typename VariableType>
145 const VariableType& operator[](const Key<VariableType>&) &&;
146
147 /// @cond
148 // No longer supported, use `config[key]` instead
149 template <typename T>
150 const T& Get() const& USERVER_IMPL_LIFETIME_BOUND;
151
152 // No longer supported, use `config[key]` instead
153 template <typename T>
154 const T& Get() &&;
155 /// @endcond
156
157private:
158 // for the constructor
159 friend class Source;
160 friend class impl::StorageData;
161 friend struct Diff;
162
163 explicit Snapshot(const impl::StorageData& storage);
164
165 const impl::SnapshotData& GetData() const;
166
167 struct Impl;
168 utils::FastPimpl<Impl, 16, 8> impl_;
169};
170
171// ========================== Implementation follows ==========================
172
173constexpr DefaultAsJsonString::DefaultAsJsonString(std::string_view json_string)
174 : json_string(json_string)
175{}
176
177template <typename T>
178ConfigDefault::ConfigDefault(std::string_view name, const T& value)
179 : name(name),
180 default_json(impl::ToJsonString(value))
181{}
182
183template <typename Variable>
184Key<Variable>::Key(std::string_view name, const VariableType& default_value)
185 : id_(impl::Register(
186 std::string{name},
187 [name = std::string{name}](const auto& docs_map) -> std::any {
188 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
189 },
190 impl::ValueToDocsMapString(name, default_value)
191 ))
192{}
193
194template <typename Variable>
195Key<Variable>::Key(std::string_view name, DefaultAsJsonString default_json)
196 : id_(impl::Register(
197 std::string{name},
198 [name = std::string{name}](const auto& docs_map) -> std::any {
199 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
200 },
201 impl::SingleToDocsMapString(name, default_json.json_string)
202 ))
203{}
204
205template <typename Variable>
206Key<Variable>::Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json)
207 : id_(impl::Register(
208 std::string{name},
209 [name = std::string{name}, parser](const auto& docs_map) -> std::any {
210 return parser(impl::DocsMapGet(docs_map, name));
211 },
212 impl::SingleToDocsMapString(name, default_json.json_string)
213 ))
214{}
215
216template <typename Variable>
217template <std::size_t N>
218Key<Variable>::Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N])
219 : id_(impl::Register(
220 std::string{},
221 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
222 impl::MultipleToDocsMapString(default_json_map, N)
223 ))
224{}
225
226template <typename Variable>
227Key<Variable>::Key(ConstantConfig /*tag*/, VariableType value)
228 : id_(impl::Register(std::string{}, [value = std::move(value)](const DocsMap& /*unused*/) { return value; }, "{}"))
229{}
230
231template <typename Variable>
232Key<Variable>::Key(impl::InternalTag, std::string_view name)
233 : id_(impl::Register(
234 std::string{name},
235 [name = std::string{name}](const auto& docs_map) -> std::any {
236 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
237 },
238 "{}"
239 ))
240{}
241
242template <typename Variable>
243Key<Variable>::Key(impl::InternalTag, DocsMapParser parser)
244 : id_(impl::Register(
245 std::string{},
246 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
247 "{}"
248 ))
249{}
250
251template <typename VariableType>
252std::string_view Key<VariableType>::GetName() const noexcept {
253 return impl::GetName(id_);
254}
255
256template <typename VariableType>
257VariableType Key<VariableType>::Parse(const DocsMap& docs_map) const {
258 return std::any_cast<VariableType>(impl::MakeConfig(id_, docs_map));
259}
260
261template <typename VariableType>
262const VariableType& Snapshot::operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND {
263 return GetData().Get<VariableType>(impl::ConfigIdGetter::Get(key));
264}
265
266template <typename VariableType>
267const VariableType& Snapshot::operator[](const Key<VariableType>&) && {
268 static_assert(!sizeof(VariableType), "keep the Snapshot before using, please");
269}
270
271template <typename T>
272const T& Snapshot::Get() const& USERVER_IMPL_LIFETIME_BOUND {
273 return (*this)[T::kDeprecatedKey];
274}
275
276template <typename T>
277const T& Snapshot::Get() && {
278 static_assert(!sizeof(T), "keep the Snapshot before using, please");
279}
280
281} // namespace dynamic_config
282
283USERVER_NAMESPACE_END