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 IsInt32() const;
143 bool IsInt64() const;
144 bool IsDouble() const;
145 bool IsString() const;
146 bool IsDateTime() const;
147 bool IsOid() const;
148 bool IsBinary() const;
149 bool IsDecimal128() const;
150 bool IsMinKey() const;
151 bool IsMaxKey() const;
152 bool IsTimestamp() const;
153
154 bool IsObject() const { return IsDocument(); }
155 /// @}
156
157 // clang-format off
158
159 /// Extracts the specified type with strict type checks
160 ///
161 /// ## Example usage:
162 ///
163 /// @snippet formats/bson/value_test.cpp Sample formats::bson::Value::As<T>() usage
164 ///
165 /// @see @ref scripts/docs/en/userver/formats.md
166
167 // clang-format on
168
169 template <typename T>
170 auto As() const {
171 static_assert(
172 formats::common::impl::kHasParse<Value, T>,
173 "There is no `Parse(const Value&, formats::parse::To<T>)` in namespace "
174 "of `T` or `formats::parse`. "
175 "Probably you have not provided a `Parse` function overload."
176 );
177
178 return Parse(*this, formats::parse::To<T>{});
179 }
180
181 /// Extracts the specified type with strict type checks, or constructs the
182 /// default value when the field is not present
183 template <typename T, typename First, typename... Rest>
184 auto As(First&& default_arg, Rest&&... more_default_args) const {
185 if (IsMissing() || IsNull()) {
186 // intended raw ctor call, sometimes casts
187 // NOLINTNEXTLINE(google-readability-casting)
188 return decltype(As<T>())(std::forward<First>(default_arg), std::forward<Rest>(more_default_args)...);
189 }
190 return As<T>();
191 }
192
193 /// @brief Returns value of *this converted to T or T() if this->IsMissing().
194 /// @throw Anything derived from std::exception.
195 /// @note Use as `value.As<T>({})`
196 template <typename T>
197 auto As(DefaultConstructed) const {
198 return (IsMissing() || IsNull()) ? decltype(As<T>())() : As<T>();
199 }
200
201 /// @brief Extracts the specified type with relaxed type checks.
202 /// For example, `true` may be converted to 1.0.
203 template <typename T>
204 T ConvertTo() const {
205 if constexpr (formats::common::impl::kHasConvert<Value, T>) {
206 return Convert(*this, formats::parse::To<T>{});
207 } else if constexpr (formats::common::impl::kHasParse<Value, T>) {
208 return Parse(*this, formats::parse::To<T>{});
209 } else {
210 static_assert(
211 !sizeof(T),
212 "There is no `Convert(const Value&, formats::parse::To<T>)` or"
213 "`Parse(const Value&, formats::parse::To<T>)`"
214 "in namespace of `T` or `formats::parse`. "
215 "Probably you have not provided a `Convert` function overload."
216 );
217 }
218 }
219
220 /// Extracts the specified type with strict type checks, or constructs the
221 /// default value when the field is not present
222 template <typename T, typename First, typename... Rest>
223 T ConvertTo(First&& default_arg, Rest&&... more_default_args) const {
224 if (IsMissing() || IsNull()) {
225 // NOLINTNEXTLINE(google-readability-casting)
226 return T(std::forward<First>(default_arg), std::forward<Rest>(more_default_args)...);
227 }
228 return ConvertTo<T>();
229 }
230
231 /// @brief Changes parsing behavior when duplicate fields are encountered.
232 /// Should not be used normally.
233 /// @details Should be called before the first field access. Only affects
234 /// documents. Default policy is to throw an exception when duplicate fields
235 /// are encountered.
236 /// @warning At most one value will be read, all others will be discarded and
237 /// cannot be serialized back!
239
240 /// Throws a MemberMissingException if the selected element does not exist
241 void CheckNotMissing() const;
242
243 /// @brief Throws a TypeMismatchException if the selected element
244 /// is not an array or null
245 void CheckArrayOrNull() const;
246
247 /// @brief Throws a TypeMismatchException if the selected element
248 /// is not a document or null
250
251 /// @cond
252 /// Same, for parsing capabilities
253 void CheckObjectOrNull() const { CheckDocumentOrNull(); }
254
255 /// @brief Returns an array as its internal representation (BSON document),
256 /// internal use only
257 Document GetInternalArrayDocument() const;
258 /// @endcond
259
260protected:
261 const impl::BsonHolder& GetBson() const;
262
263private:
264 friend class ValueBuilder;
265 friend class impl::BsonBuilder;
266
267 friend bool Parse(const Value& value, parse::To<bool>);
268 friend int64_t Parse(const Value& value, parse::To<int64_t>);
269 friend uint64_t Parse(const Value& value, parse::To<uint64_t>);
270 friend double Parse(const Value& value, parse::To<double>);
271 friend std::string Parse(const Value& value, parse::To<std::string>);
272 friend std::chrono::system_clock::time_point
273 Parse(const Value& value, parse::To<std::chrono::system_clock::time_point>);
274 friend Oid Parse(const Value& value, parse::To<Oid>);
275 friend Binary Parse(const Value& value, parse::To<Binary>);
276 friend Decimal128 Parse(const Value& value, parse::To<Decimal128>);
277 friend Timestamp Parse(const Value& value, parse::To<Timestamp>);
278 friend Document Parse(const Value& value, parse::To<Document>);
279
280 impl::ValueImplPtr impl_;
281};
282
283/// @cond
284bool Parse(const Value& value, parse::To<bool>);
285
286int64_t Parse(const Value& value, parse::To<int64_t>);
287
288uint64_t Parse(const Value& value, parse::To<uint64_t>);
289
290double Parse(const Value& value, parse::To<double>);
291
292std::string Parse(const Value& value, parse::To<std::string>);
293
294std::chrono::system_clock::time_point Parse(const Value& value, parse::To<std::chrono::system_clock::time_point>);
295
296Oid Parse(const Value& value, parse::To<Oid>);
297
298Binary Parse(const Value& value, parse::To<Binary>);
299
300Decimal128 Parse(const Value& value, parse::To<Decimal128>);
301
302Timestamp Parse(const Value& value, parse::To<Timestamp>);
303
304Document Parse(const Value& value, parse::To<Document>);
305
306template <>
307bool Value::ConvertTo<bool>() const;
308
309template <>
310int64_t Value::ConvertTo<int64_t>() const;
311
312template <>
313uint64_t Value::ConvertTo<uint64_t>() const;
314
315template <>
316double Value::ConvertTo<double>() const;
317
318template <>
319std::string Value::ConvertTo<std::string>() const;
320/// @endcond
321
322/// @brief Wrapper for handy python-like iteration over a map
323///
324/// @code
325/// for (const auto& [name, value]: Items(map)) ...
326/// @endcode
327using formats::common::Items;
328
329} // namespace formats::bson
330
331/// Although we provide user defined literals, please beware that
332/// 'using namespace ABC' may contradict code style of your company.
333namespace formats::literals {
334
335bson::Value operator"" _bson(const char* str, std::size_t len);
336
337} // namespace formats::literals
338
339USERVER_NAMESPACE_END