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