userver: userver/utils/default_dict.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
default_dict.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/default_dict.hpp
4/// @brief Dictionary with special value for missing keys
5
6#include <optional>
7#include <string>
8
9#include <userver/formats/json/value.hpp>
10#include <userver/formats/parse/common_containers.hpp>
11#include <userver/formats/serialize/common_containers.hpp>
12#include <userver/utils/impl/transparent_hash.hpp>
13
14USERVER_NAMESPACE_BEGIN
15
16namespace utils {
17
18/// Name of the key with default value for utils::DefaultDict
19inline constexpr std::string_view kDefaultDictDefaultName = "__default__";
20
21namespace impl {
22
23[[noreturn]] void ThrowNoValueException(std::string_view dict_name,
24 std::string_view key);
25
26} // namespace impl
27
28/// @ingroup userver_universal userver_containers
29///
30/// @brief Dictionary that for missing keys falls back to a default value
31/// stored by key utils::kDefaultDictDefaultName.
32///
33/// Usually used by dynamic configs for providing a fallback or default value
34/// along with the specific values. For example:
35/// @code{.json}
36/// {
37/// ".htm": "text/html",
38/// ".html": "text/html",
39/// "__default__": "text/plain"
40/// }
41/// @endcode
42template <typename ValueType>
43class DefaultDict final {
44 public:
45 using DictType = utils::impl::TransparentMap<std::string, ValueType>;
46 using const_iterator = typename DictType::const_iterator;
47 using iterator = const_iterator;
48 using value_type = typename DictType::value_type;
49 using key_type = std::string;
50 using mapped_type = ValueType;
51 using init_list =
52 std::initializer_list<std::pair<std::string_view, ValueType>>;
53
54 DefaultDict() = default;
55
56 DefaultDict(init_list contents) : dict_(contents.begin(), contents.end()) {}
57
58 DefaultDict(DictType dict) : dict_(std::move(dict)) {}
59
60 DefaultDict(std::string name, init_list contents)
61 : name_(std::move(name)), dict_(contents.begin(), contents.end()) {}
62
63 DefaultDict(std::string name, DictType dict)
64 : name_(std::move(name)), dict_(std::move(dict)) {}
65
66 /// Returns true if *this has a utils::kDefaultDictDefaultName key,
67 /// otherwise returns false.
68 bool HasDefaultValue() const noexcept {
69 return HasValue(kDefaultDictDefaultName);
70 }
71
72 /// Returns true if *this has `key`, otherwise returns false.
73 bool HasValue(std::string_view key) const noexcept {
74 return utils::impl::FindTransparent(dict_, key) != dict_.end();
75 }
76
77 /// Returns value by utils::kDefaultDictDefaultName key,
78 /// throws a std::runtime_error exception.
79 const ValueType& GetDefaultValue() const {
80 const auto it =
81 utils::impl::FindTransparent(dict_, kDefaultDictDefaultName);
82 if (it == dict_.end()) {
83 impl::ThrowNoValueException(name_, kDefaultDictDefaultName);
84 }
85 return it->second;
86 }
87
88 /// Returns value by `key` if it is in *this, otherwise returns value
89 /// by utils::kDefaultDictDefaultName if it is in *this,
90 /// otherwise throws a std::runtime_error exception.
91 const ValueType& operator[](std::string_view key) const {
92 auto it = utils::impl::FindTransparent(dict_, key);
93 if (it == dict_.end()) {
94 it = utils::impl::FindTransparent(dict_, kDefaultDictDefaultName);
95 if (it == dict_.end()) {
96 impl::ThrowNoValueException(name_, key);
97 }
98 }
99 return it->second;
100 }
101
102 /// Returns `key ? Get(*key) : GetDefaultValue()`
103 template <typename StringType>
104 const ValueType& operator[](const std::optional<StringType>& key) const {
105 return key ? Get(*key) : GetDefaultValue();
106 }
107
108 /// @overload operator[](std::string_view key)
109 const ValueType& Get(std::string_view key) const { return (*this)[key]; }
110
111 /// Returns `key ? Get(*key) : GetDefaultValue()`
112 template <typename StringType>
113 const ValueType& Get(const std::optional<StringType>& key) const {
114 return (*this)[key];
115 }
116
117 /// Returns value by `key` if it is in *this, otherwise returns value
118 /// by utils::kDefaultDictDefaultName if it is in *this,
119 /// otherwise returns an empty optional.
120 std::optional<ValueType> GetOptional(std::string_view key) const {
121 auto it = utils::impl::FindTransparent(dict_, key);
122 if (it == dict_.end()) {
123 it = utils::impl::FindTransparent(dict_, kDefaultDictDefaultName);
124 if (it == dict_.end()) return std::nullopt;
125 }
126
127 return it->second;
128 }
129
130 /// Sets the default value.
131 ///
132 /// The function is primarily there for testing purposes - DefaultDict is
133 /// normally obtained by parsing the config.
134 void SetDefault(ValueType value) {
135 Set(kDefaultDictDefaultName, std::move(value));
136 }
137
138 /// Sets a mapping. key == utils::kDefaultDictDefaultName is allowed.
139 ///
140 /// The function is primarily there for testing purposes - DefaultDict is
141 /// normally obtained by parsing the config.
142 template <typename StringType>
143 void Set(StringType&& key, ValueType value) {
144 utils::impl::TransparentInsertOrAssign(dict_, std::forward<StringType>(key),
145 std::move(value));
146 }
147
148 auto begin() const noexcept { return dict_.begin(); }
149
150 auto end() const noexcept { return dict_.end(); }
151
152 const std::string& GetName() const noexcept { return name_; }
153
154 bool operator==(const DefaultDict& r) const noexcept {
155 return dict_ == r.dict_;
156 }
157
158 bool operator!=(const DefaultDict& r) const noexcept { return !(*this == r); }
159
160 private:
161 std::string name_;
162 DictType dict_;
163};
164
165template <typename Value, typename T>
166std::enable_if_t<formats::common::kIsFormatValue<Value>, DefaultDict<T>> Parse(
167 const Value& value, formats::parse::To<DefaultDict<T>>) {
168 return DefaultDict<T>{value.GetPath(),
169 value.template As<typename DefaultDict<T>::DictType>()};
170}
171
172} // namespace utils
173
174USERVER_NAMESPACE_END