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/// @brief The shared snapshot of
112/// @ref scripts/docs/en/userver/dynamic_config.md "dynamic configs". Cheap to
113/// copy, even cheaper to move. Thread safe, not updated with new dynamic
114/// config values in background (it's a snapshot!).
115///
116/// When a config update comes in via new `DocsMap`, configs of all
117/// the registered types are constructed and stored in `Config`. After that
118/// the `DocsMap` is dropped.
119///
120/// Config types are automatically registered if they are used
121/// somewhere in the program.
122///
123/// ## Usage example:
124/// @snippet components/component_sample_test.cpp Sample user component runtime config source
125class Snapshot final {
126public:
127 Snapshot(const Snapshot&);
128 Snapshot& operator=(const Snapshot&);
129
130 Snapshot(Snapshot&&) noexcept;
131 Snapshot& operator=(Snapshot&&) noexcept;
132
133 ~Snapshot();
134
135 /// Used to access individual configs in the type-safe config map
136 template <typename VariableType>
137 const VariableType& operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND;
138
139 /// Used to access individual configs in the type-safe config map
140 template <typename VariableType>
141 const VariableType& operator[](const Key<VariableType>&) &&;
142
143 /// @cond
144 // No longer supported, use `config[key]` instead
145 template <typename T>
146 const T& Get() const& USERVER_IMPL_LIFETIME_BOUND;
147
148 // No longer supported, use `config[key]` instead
149 template <typename T>
150 const T& Get() &&;
151 /// @endcond
152
153private:
154 // for the constructor
155 friend class Source;
156 friend class impl::StorageData;
157 friend struct Diff;
158
159 explicit Snapshot(const impl::StorageData& storage);
160
161 const impl::SnapshotData& GetData() const;
162
163 struct Impl;
164 utils::FastPimpl<Impl, 16, 8> impl_;
165};
166
167// ========================== Implementation follows ==========================
168
169constexpr DefaultAsJsonString::DefaultAsJsonString(std::string_view json_string)
170 : json_string(json_string)
171{}
172
173template <typename T>
174ConfigDefault::ConfigDefault(std::string_view name, const T& value)
175 : name(name),
176 default_json(impl::ToJsonString(value))
177{}
178
179template <typename Variable>
180Key<Variable>::Key(std::string_view name, const VariableType& default_value)
181 : id_(impl::Register(
182 std::string{name},
183 [name = std::string{name}](const auto& docs_map) -> std::any {
184 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
185 },
186 impl::ValueToDocsMapString(name, default_value)
187 ))
188{}
189
190template <typename Variable>
191Key<Variable>::Key(std::string_view name, DefaultAsJsonString default_json)
192 : id_(impl::Register(
193 std::string{name},
194 [name = std::string{name}](const auto& docs_map) -> std::any {
195 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
196 },
197 impl::SingleToDocsMapString(name, default_json.json_string)
198 ))
199{}
200
201template <typename Variable>
202Key<Variable>::Key(std::string_view name, JsonParser parser, DefaultAsJsonString default_json)
203 : id_(impl::Register(
204 std::string{name},
205 [name = std::string{name}, parser](const auto& docs_map) -> std::any {
206 return parser(impl::DocsMapGet(docs_map, name));
207 },
208 impl::SingleToDocsMapString(name, default_json.json_string)
209 ))
210{}
211
212template <typename Variable>
213template <std::size_t N>
214Key<Variable>::Key(DocsMapParser parser, const ConfigDefault (&default_json_map)[N])
215 : id_(impl::Register(
216 std::string{},
217 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
218 impl::MultipleToDocsMapString(default_json_map, N)
219 ))
220{}
221
222template <typename Variable>
223Key<Variable>::Key(ConstantConfig /*tag*/, VariableType value)
224 : id_(impl::Register(std::string{}, [value = std::move(value)](const DocsMap& /*unused*/) { return value; }, "{}"))
225{}
226
227template <typename Variable>
228Key<Variable>::Key(impl::InternalTag, std::string_view name)
229 : id_(impl::Register(
230 std::string{name},
231 [name = std::string{name}](const auto& docs_map) -> std::any {
232 return impl::DocsMapGet(docs_map, name).template As<VariableType>();
233 },
234 "{}"
235 ))
236{}
237
238template <typename Variable>
239Key<Variable>::Key(impl::InternalTag, DocsMapParser parser)
240 : id_(impl::Register(
241 std::string{},
242 [parser](const DocsMap& docs_map) -> std::any { return parser(docs_map); },
243 "{}"
244 ))
245{}
246
247template <typename VariableType>
248std::string_view Key<VariableType>::GetName() const noexcept {
249 return impl::GetName(id_);
250}
251
252template <typename VariableType>
253VariableType Key<VariableType>::Parse(const DocsMap& docs_map) const {
254 return std::any_cast<VariableType>(impl::MakeConfig(id_, docs_map));
255}
256
257template <typename VariableType>
258const VariableType& Snapshot::operator[](const Key<VariableType>& key) const& USERVER_IMPL_LIFETIME_BOUND {
259 return GetData().Get<VariableType>(impl::ConfigIdGetter::Get(key));
260}
261
262template <typename VariableType>
263const VariableType& Snapshot::operator[](const Key<VariableType>&) && {
264 static_assert(!sizeof(VariableType), "keep the Snapshot before using, please");
265}
266
267template <typename T>
268const T& Snapshot::Get() const& USERVER_IMPL_LIFETIME_BOUND {
269 return (*this)[T::kDeprecatedKey];
270}
271
272template <typename T>
273const T& Snapshot::Get() && {
274 static_assert(!sizeof(T), "keep the Snapshot before using, please");
275}
276
277} // namespace dynamic_config
278
279USERVER_NAMESPACE_END