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/dynamic_config/registered_config_meta.hpp>
14#include <userver/formats/json_fwd.hpp>
15#include <userver/utils/fast_pimpl.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace dynamic_config {
20
21/// A strong typedef for usage in dynamic_config::Key constructors.
22struct DefaultAsJsonString final {
23 constexpr explicit DefaultAsJsonString(std::string_view json_string);
24
25 std::string_view json_string;
26};
27
28/// A config name-value pair for usage in dynamic_config::Key constructors.
29struct ConfigDefault final {
30 template <typename T>
31 ConfigDefault(std::string_view name, const T& value);
32
33 ConfigDefault(std::string_view name, DefaultAsJsonString default_json);
34
35 std::string_view name;
36 std::string default_json;
37};
38
39/// A tag type for usage in dynamic_config::Key constructors.
40struct ConstantConfig final {
41 constexpr explicit ConstantConfig() = default;
42};
43
44/// @brief A config key is a unique identifier for a config variable
45/// @snippet core/src/dynamic_config/config_test.cpp key bool
46template <typename Variable>
47class Key final {
48public:
49 /// The type of the parsed config variable.
50 using VariableType = Variable;
51
52 using JsonParser = Variable (*)(const formats::json::Value&);
53 using DocsMapParser = Variable (*)(const DocsMap&);
54
55 /// @brief The constructor for a trivial `VariableType`, e.g. `bool`, integer,
56 /// `double`, `string`. The default is passed by value.
57 ///
58 /// Usage example:
59 /// @snippet core/src/dynamic_config/config_test.cpp key bool
60 Key(std::string_view name, const VariableType& default_value);
61
62 /// @brief The constructor for a non-trivial `VariableType`. The default is
63 /// passed as a JSON string.
64 ///
65 /// Uses formats::json::Value `Parse` customization point function to parse
66 /// `VariableType`.
67 ///
68 /// Usage example:
69 /// @snippet core/src/dynamic_config/config_test.cpp struct config cpp
70 Key(std::string_view name, DefaultAsJsonString default_json);
71
72 /// @brief The constructor that provides a special parser from JSON.
73 /// @warning Prefer the constructors above whenever possible.
74 /// @details Can be used when generic `Parse` is not applicable. Sometimes
75 /// used to add validation, e.g. minimum, maximum, string pattern, etc.
76 Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json);
77
78 /// @brief Constructor with a schema_hash, used by chaotic codegen.
79 /// @param name config variable name
80 /// @param parser custom JSON parser for the variable
81 /// @param default_json default value as a JSON string
82 /// @param schema_hash SHA-256(canonical JSON of schema with inlined definitions),
83 /// lowercase hex, 64 chars. Stored in the global registry and
84 /// retrievable via dynamic_config::impl::GetRegisteredConfigsMeta()
85 /// as dynamic_config::RegisteredConfigMeta::schema_hash.
86 /// @warning This constructor is intended for use by chaotic codegen only,
87 /// not for manual calls. Backwards-incompatible changes may be
88 /// introduced in the future, e.g. adding additional parameters.
89 Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json, std::string_view schema_hash);
90
91 /// @brief The constructor that parses multiple JSON config items
92 /// into a single C++ object.
93 /// @warning Prefer to use a separate `Key` per JSON config item and use the
94 /// constructors above whenever possible.
95 template <std::size_t N>
96 Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N]);
97
98 /// Creates a config that always has the same value.
99 Key(ConstantConfig, VariableType value);
100
101 /// @cond
102 Key(impl::InternalTag, std::string_view name);
103
104 Key(impl::InternalTag, DocsMapParser parser);
105 /// @endcond
106
107 Key(const Key&) noexcept = delete;
108 Key& operator=(const Key&) noexcept = delete;
109
110 /// @returns the name of the config variable, as passed at the construction.
111 std::string_view GetName() const noexcept;
112
113 /// Parses the config. Useful only in some very niche scenarios. The config
114 /// value should be typically be retrieved from dynamic_config::Snapshot,
115 /// which is obtained from components::DynamicConfig in production or from
116 /// dynamic_config::StorageMock in unit tests.
117 VariableType Parse(const DocsMap& docs_map) const;
118
119private:
120 friend struct impl::ConfigIdGetter;
121
122 const impl::ConfigId id_;
123};
124
125/// @brief The shared snapshot of
126/// @ref scripts/docs/en/userver/dynamic_config.md "dynamic configs". Cheap to
127/// copy, even cheaper to move. Thread safe, not updated with new dynamic
128/// config values in background (it's a snapshot!).
129///
130/// When a config update comes in via new `DocsMap`, configs of all
131/// the registered types are constructed and stored in `Config`. After that
132/// the `DocsMap` is dropped.
133///
134/// Config types are automatically registered if they are used
135/// somewhere in the program.
136///
137/// ## Usage example:
138/// @snippet core/src/components/component_sample_test.cpp Sample user component runtime config source
139class Snapshot final {
140public:
141 Snapshot(const Snapshot&);
142 Snapshot& operator=(const Snapshot&);
143
144 Snapshot(Snapshot&&) noexcept;
145 Snapshot& operator=(Snapshot&&) noexcept;
146
147 ~Snapshot();
148
149 /// Used to access individual configs in the type-safe config map
150 template <typename VariableType>
151 const VariableType& operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND;
152
153 /// Used to access individual configs in the type-safe config map
154 template <typename VariableType>
155 const VariableType& operator[](const Key<VariableType>&) &&;
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>
217Key<Variable>::Key(
218 std::string_view name,
219 JsonParser parser,
220 DefaultAsJsonString default_json,
221 std::string_view schema_hash
222)
223 : id_(impl::Register(
224 std::string{name},
225 [name = std::string{name}, parser](const auto& docs_map) -> std::any {
226 return parser(impl::DocsMapGet(docs_map, name));
227 },
228 impl::SingleToDocsMapString(name, default_json.json_string),
229 std::string{schema_hash}
230 ))
231{}
232
233template <typename Variable>
234template <std::size_t N>
235Key<Variable>::Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N])
236 : id_(impl::Register(
237 std::string{},
238 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
239 impl::MultipleToDocsMapString(default_json_map, N)
240 ))
241{}
242
243template <typename Variable>
244Key<Variable>::Key(ConstantConfig /*tag*/, VariableType value)
245 : id_(impl::Register(std::string{}, [value = std::move(value)](const DocsMap& /*unused*/) { return value; }, "{}"))
246{}
247
248template <typename Variable>
249Key<Variable>::Key(impl::InternalTag, std::string_view name)
250 : id_(impl::Register(
251 std::string{name},
252 [name = std::string{name}](const auto& docs_map) -> std::any {
253 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
254 },
255 "{}"
256 ))
257{}
258
259template <typename Variable>
260Key<Variable>::Key(impl::InternalTag, DocsMapParser parser)
261 : id_(impl::Register(
262 std::string{},
263 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
264 "{}"
265 ))
266{}
267
268template <typename VariableType>
269std::string_view Key<VariableType>::GetName() const noexcept {
270 return impl::GetName(id_);
271}
272
273template <typename VariableType>
274VariableType Key<VariableType>::Parse(const DocsMap& docs_map) const {
275 return std::any_cast<VariableType>(impl::MakeConfig(id_, docs_map));
276}
277
278template <typename VariableType>
279const VariableType& Snapshot::operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND {
280 return GetData().Get<VariableType>(impl::ConfigIdGetter::Get(key));
281}
282
283template <typename VariableType>
284const VariableType& Snapshot::operator[](const Key<VariableType>&) && {
285 static_assert(!sizeof(VariableType), "keep the Snapshot before using, please");
286}
287
288} // namespace dynamic_config
289
290USERVER_NAMESPACE_END