userver: userver/formats/bson/value.hpp Source File
Loading...
Searching...
No Matches
value.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/bson/value.hpp
4/// @brief @copybrief formats::bson::Value
5
6#include <chrono>
7#include <cstddef>
8#include <cstdint>
9#include <string>
10
11#include <userver/formats/bson/exception.hpp>
12#include <userver/formats/bson/iterator.hpp>
13#include <userver/formats/bson/types.hpp>
14#include <userver/formats/common/items.hpp>
15#include <userver/formats/common/meta.hpp>
16#include <userver/formats/parse/common.hpp>
17#include <userver/formats/parse/common_containers.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21namespace formats::bson {
22namespace impl {
23class BsonBuilder;
24class ValueImpl;
25} // namespace impl
26
27class Document;
28class ValueBuilder;
29
30/// @brief Non-mutable BSON value representation.
31///
32/// Class provides non mutable access BSON value. For modification and
33/// construction of new BSON values use formats::bson::ValueBuilder.
34///
35/// ## Example usage:
36///
37/// @snippet formats/bson/value_test.cpp Sample formats::bson::Value usage
38///
39/// @see @ref scripts/docs/en/userver/formats.md
40///
41/// To iterate over `Value` as object use formats::common::Items.
42class Value {
43public:
45
46 using const_iterator = Iterator<const Value, common::IteratorDirection::kForward>;
47 using const_reverse_iterator = Iterator<const Value, common::IteratorDirection::kReverse>;
48 using Exception = formats::bson::BsonException;
49 using ParseException = formats::bson::ParseException;
50 using ExceptionWithPath = formats::bson::ExceptionWithPath;
51 using Builder = ValueBuilder;
52
53 /// @brief Selectors for duplicate fields parsing behavior
54 /// @see SetDuplicateFieldsPolicy
55 enum class DuplicateFieldsPolicy { kForbid, kUseFirst, kUseLast };
56
57 /// Constructs a `null` value
59
60 Value(const Value&) = default;
61 Value(Value&&) noexcept = default;
62 Value& operator=(const Value&) & = default;
63 Value& operator=(Value&&) & noexcept = default;
64
65 template <class T>
66 Value& operator=(T&&) && {
67 static_assert(
68 !sizeof(T),
69 "You're assigning to a temporary formats::bson::Value! Use "
70 "formats::bson::ValueBuilder for data modifications."
71 );
72 return *this;
73 }
74
75 /// @cond
76 /// Constructor from implementation, internal use only
77 explicit Value(impl::ValueImplPtr);
78 /// @endcond
79
80 /// @brief Retrieves document field by name
81 /// @param name field name
82 /// @throws TypeMismatchException if value is not a missing value, a document,
83 /// or `null`
84 Value operator[](const std::string& name) const;
85
86 /// @brief Retrieves array element by index
87 /// @param index element index
88 /// @throws TypeMismatchException if value is not an array or `null`
89 /// @throws OutOfBoundsException if index is invalid for the array
90 Value operator[](uint32_t index) const;
91
92 /// @brief Checks whether the document has a field
93 /// @param name field name
94 /// @throws TypeMismatchExcepiton if value is not a document or `null`
95 bool HasMember(const std::string& name) const;
96
97 /// @brief Returns an iterator to the first array element/document field
98 /// @throws TypeMismatchException if value is not a document, array or `null`
99 ///
100 /// To iterate over `Value` as object use formats::common::Items.
101 const_iterator begin() const;
102
103 /// @brief Returns an iterator following the last array element/document field
104 /// @throws TypeMismatchException if value is not a document, array or `null`
105 const_iterator end() const;
106
107 /// @brief Returns a reversed iterator to the last array element
108 /// @throws TypeMismatchException if value is not an array or `null`
109 const_reverse_iterator rbegin() const;
110
111 /// @brief Returns a reversed iterator following the first array element
112 /// @throws TypeMismatchException if value is not an array or `null`
113 const_reverse_iterator rend() const;
114
115 /// @brief Returns whether the document/array is empty
116 /// @throws TypeMismatchException if value is not a document, array or `null`
117 /// @note Returns `true` for `null`.
118 bool IsEmpty() const;
119
120 /// @brief Returns the number of elements in a document/array
121 /// @throws TypeMismatchException if value is not a document, array or `null`
122 /// @note May require linear time before the first element access.
123 /// @note Returns 0 for `null`.
124 uint32_t GetSize() const;
125
126 /// Returns value path in a document
127 std::string GetPath() const;
128
129 bool operator==(const Value&) const;
130 bool operator!=(const Value&) const;
131
132 /// @brief Checks whether the selected element exists
133 /// @note MemberMissingException is throws on nonexisting element access
134 bool IsMissing() const;
135
136 /// @name Type checking
137 /// @{
138 bool IsArray() const;
139 bool IsDocument() const;
140 bool IsNull() const;
141 bool IsBool() const;
142 bool IsInt() const;
143 bool IsInt32() const;
144 bool IsInt64() const;
145 bool IsUInt64() const;
146 bool IsDouble() const;
147 bool IsString() const;
148 bool IsDateTime() const;
149 bool IsOid() const;
150 bool IsBinary() const;
151 bool IsDecimal128() const;
152 bool IsMinKey() const;
153 bool IsMaxKey() const;
154 bool IsTimestamp() const;
155
156 bool IsObject() const { return IsDocument(); }
157 /// @}
158
159 // clang-format off
160
161 /// Extracts the specified type with strict type checks
162 ///
163 /// ## Example usage:
164 ///
165 /// @snippet formats/bson/value_test.cpp Sample formats::bson::Value::As<T>() usage
166 ///
167 /// @see @ref scripts/docs/en/userver/formats.md
168
169 // clang-format on
170
171 template <typename T>
172 auto As() const {
173 static_assert(
174 formats::common::impl::kHasParse<Value, T>,
175 "There is no `Parse(const Value&, formats::parse::To<T>)` in namespace "
176 "of `T` or `formats::parse`. "
177 "Probably you have not provided a `Parse` function overload."
178 );
179
180 return Parse(*this, formats::parse::To<T>{});
181 }
182
183 /// Extracts the specified type with strict type checks, or constructs the
184 /// default value when the field is not present
185 template <typename T, typename First, typename... Rest>
186 auto As(First&& default_arg, Rest&&... more_default_args) const {
187 if (IsMissing() || IsNull()) {
188 // intended raw ctor call, sometimes casts
189 // NOLINTNEXTLINE(google-readability-casting)
190 return decltype(As<T>())(std::forward<First>(default_arg), std::forward<Rest>(more_default_args)...);
191 }
192 return As<T>();
193 }
194
195 /// @brief Returns value of *this converted to T or T() if this->IsMissing().
196 /// @throw Anything derived from std::exception.
197 /// @note Use as `value.As<T>({})`
198 template <typename T>
199 auto As(DefaultConstructed) const {
200 return (IsMissing() || IsNull()) ? decltype(As<T>())() : As<T>();
201 }
202
203 /// @brief Extracts the specified type with relaxed type checks.
204 /// For example, `true` may be converted to 1.0.
205 template <typename T>
206 T ConvertTo() const {
207 if constexpr (formats::common::impl::kHasConvert<Value, T>) {
208 return Convert(*this, formats::parse::To<T>{});
209 } else if constexpr (formats::common::impl::kHasParse<Value, T>) {
210 return Parse(*this, formats::parse::To<T>{});
211 } else {
212 static_assert(
213 !sizeof(T),
214 "There is no `Convert(const Value&, formats::parse::To<T>)` or"
215 "`Parse(const Value&, formats::parse::To<T>)`"
216 "in namespace of `T` or `formats::parse`. "
217 "Probably you have not provided a `Convert` function overload."
218 );
219 }
220 }
221
222 /// Extracts the specified type with strict type checks, or constructs the
223 /// default value when the field is not present
224 template <typename T, typename First, typename... Rest>
225 T ConvertTo(First&& default_arg, Rest&&... more_default_args) const {
226 if (IsMissing() || IsNull()) {
227 // NOLINTNEXTLINE(google-readability-casting)
228 return T(std::forward<First>(default_arg), std::forward<Rest>(more_default_args)...);
229 }
230 return ConvertTo<T>();
231 }
232
233 /// @brief Changes parsing behavior when duplicate fields are encountered.
234 /// Should not be used normally.
235 /// @details Should be called before the first field access. Only affects
236 /// documents. Default policy is to throw an exception when duplicate fields
237 /// are encountered.
238 /// @warning At most one value will be read, all others will be discarded and
239 /// cannot be serialized back!
241
242 /// Throws a MemberMissingException if the selected element does not exist
243 void CheckNotMissing() const;
244
245 /// @brief Throws a TypeMismatchException if the selected element
246 /// is not an array or null
247 void CheckArrayOrNull() const;
248
249 /// @brief Throws a TypeMismatchException if the selected element
250 /// is not a document or null
252
253 /// @cond
254 /// Same, for parsing capabilities
255 void CheckObjectOrNull() const { CheckDocumentOrNull(); }
256
257 /// @brief Returns an array as its internal representation (BSON document),
258 /// internal use only
259 Document GetInternalArrayDocument() const;
260 /// @endcond
261
262protected:
263 const impl::BsonHolder& GetBson() const;
264
265private:
266 friend class ValueBuilder;
267 friend class impl::BsonBuilder;
268
269 friend bool Parse(const Value& value, parse::To<bool>);
270 friend int64_t Parse(const Value& value, parse::To<int64_t>);
271 friend uint64_t Parse(const Value& value, parse::To<uint64_t>);
272 friend double Parse(const Value& value, parse::To<double>);
273 friend std::string Parse(const Value& value, parse::To<std::string>);
274 friend std::chrono::system_clock::time_point
275 Parse(const Value& value, parse::To<std::chrono::system_clock::time_point>);
276 friend Oid Parse(const Value& value, parse::To<Oid>);
277 friend Binary Parse(const Value& value, parse::To<Binary>);
278 friend Decimal128 Parse(const Value& value, parse::To<Decimal128>);
279 friend Timestamp Parse(const Value& value, parse::To<Timestamp>);
280 friend Document Parse(const Value& value, parse::To<Document>);
281
282 impl::ValueImplPtr impl_;
283};
284
285/// @cond
286bool Parse(const Value& value, parse::To<bool>);
287
288int64_t Parse(const Value& value, parse::To<int64_t>);
289
290uint64_t Parse(const Value& value, parse::To<uint64_t>);
291
292double Parse(const Value& value, parse::To<double>);
293
294std::string Parse(const Value& value, parse::To<std::string>);
295
296std::chrono::system_clock::time_point Parse(const Value& value, parse::To<std::chrono::system_clock::time_point>);
297
298Oid Parse(const Value& value, parse::To<Oid>);
299
300Binary Parse(const Value& value, parse::To<Binary>);
301
302Decimal128 Parse(const Value& value, parse::To<Decimal128>);
303
304Timestamp Parse(const Value& value, parse::To<Timestamp>);
305
306Document Parse(const Value& value, parse::To<Document>);
307
308template <>
309bool Value::ConvertTo<bool>() const;
310
311template <>
312int64_t Value::ConvertTo<int64_t>() const;
313
314template <>
315uint64_t Value::ConvertTo<uint64_t>() const;
316
317template <>
318double Value::ConvertTo<double>() const;
319
320template <>
321std::string Value::ConvertTo<std::string>() const;
322/// @endcond
323
324/// @brief Wrapper for handy python-like iteration over a map
325///
326/// @code
327/// for (const auto& [name, value]: Items(map)) ...
328/// @endcode
329using formats::common::Items;
330
331} // namespace formats::bson
332
333/// Although we provide user defined literals, please beware that
334/// 'using namespace ABC' may contradict code style of your company.
335namespace formats::literals {
336
337bson::Value operator""_bson(const char* str, std::size_t len);
338
339} // namespace formats::literals
340
341USERVER_NAMESPACE_END