userver: userver/formats/yaml/value.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
value.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/formats/yaml/value.hpp
4/// @brief @copybrief formats::yaml::Value
5
6#include <type_traits>
7
8#include <userver/formats/common/items.hpp>
9#include <userver/formats/common/meta.hpp>
10#include <userver/formats/parse/common.hpp>
11#include <userver/formats/yaml/exception.hpp>
12#include <userver/formats/yaml/iterator.hpp>
13#include <userver/formats/yaml/types.hpp>
14#include <userver/utils/fast_pimpl.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace formats::yaml {
19
20class ValueBuilder;
21
22/// @ingroup userver_universal userver_containers userver_formats
23///
24/// @brief Non-mutable YAML value representation.
25///
26/// Class provides non mutable access YAML value. For modification and
27/// construction of new YAML values use formats::yaml::ValueBuilder.
28///
29/// ## Example usage:
30///
31/// @snippet formats/yaml/value_test.cpp Sample formats::yaml::Value usage
32///
33/// @see @ref scripts/docs/en/userver/formats.md
34class Value final {
35 public:
36 struct IterTraits {
37 using native_iter = YAML::const_iterator;
38 using value_type = formats::yaml::Value;
39 using reference = const formats::yaml::Value&;
40 using pointer = const formats::yaml::Value*;
41 };
43
44 using const_iterator = Iterator<IterTraits>;
45 using Exception = formats::yaml::Exception;
46 using ParseException = formats::yaml::ParseException;
47 using Builder = ValueBuilder;
48
49 /// @brief Constructs a Value that holds a Null.
50 Value() noexcept;
51
52 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
53 Value(Value&&);
54 Value(const Value&);
55 // NOLINTNEXTLINE(performance-noexcept-move-constructor)
56 Value& operator=(Value&&);
57 Value& operator=(const Value&);
58
59 template <class T>
60 Value& operator=(T&&) && {
61 static_assert(!sizeof(T),
62 "You're assigning to a temporary formats::yaml::Value! Use "
63 "formats::yaml::ValueBuilder for data modifications.");
64 return *this;
65 }
66
67 ~Value();
68
69 /// @brief Copies `other`, appending `path_prefix` to the stored `path`
70 /// @warning `other` must have an empty (root) path
71 /// @throw `std::logic_error` if `other` has path other than the root path
72 /// @see GetPath
73 Value(Value&& other, std::string path_prefix);
74
75 /// @brief Access member by key for read.
76 /// @throw TypeMismatchException if not a missing value, an object, or Null.
77 Value operator[](std::string_view key) const;
78
79 /// @brief Access array member by index for read.
80 /// @throw TypeMismatchException if not an array value.
81 /// @throw `OutOfBoundsException` if index is greater or equal
82 /// than size.
83 Value operator[](std::size_t index) const;
84
85 /// @brief Returns an iterator to the beginning of the held array or map.
86 /// @throw TypeMismatchException if not an array or an object.
87 const_iterator begin() const;
88
89 /// @brief Returns an iterator to the end of the held array or map.
90 /// @throw TypeMismatchException if not an array or an object.
91 const_iterator end() const;
92
93 /// @brief Returns whether the array or object is empty.
94 /// @throw TypeMismatchException if not an array or an object.
95 bool IsEmpty() const;
96
97 /// @brief Returns array size or object members count.
98 /// @throw TypeMismatchException if not an array or an object.
99 std::size_t GetSize() const;
100
101 /// @brief Compares values.
102 /// @throw MemberMissingException if `*this` or `other` is missing.
103 bool operator==(const Value& other) const;
104 bool operator!=(const Value& other) const;
105
106 /// @brief Returns true if *this holds nothing. When `IsMissing()` returns
107 /// `true` any attempt to get the actual value or iterate over *this will
108 /// throw MemberMissingException.
109 bool IsMissing() const;
110
111 /// @brief Returns true if *this holds a Null (Type::kNull).
112 bool IsNull() const noexcept;
113
114 /// @brief Returns true if *this is convertible to bool.
115 bool IsBool() const noexcept;
116
117 /// @brief Returns true if *this is convertible to int.
118 bool IsInt() const noexcept;
119
120 /// @brief Returns true if *this is convertible to int64_t.
121 bool IsInt64() const noexcept;
122
123 /// @brief Returns true if *this is convertible to uint64_t.
124 bool IsUInt64() const noexcept;
125
126 /// @brief Returns true if *this is convertible to double.
127 bool IsDouble() const noexcept;
128
129 /// @brief Returns true if *this is convertible to std::string.
130 bool IsString() const noexcept;
131
132 /// @brief Returns true if *this is an array (Type::kArray).
133 bool IsArray() const noexcept;
134
135 /// @brief Returns true if *this is a map (Type::kObject).
136 bool IsObject() const noexcept;
137
138 // clang-format off
139
140 /// @brief Returns value of *this converted to T.
141 /// @throw Anything derived from std::exception.
142 ///
143 /// ## Example usage:
144 ///
145 /// @snippet formats/yaml/value_test.cpp Sample formats::yaml::Value::As<T>() usage
146 ///
147 /// @see @ref scripts/docs/en/userver/formats.md
148
149 // clang-format on
150
151 template <typename T>
152 T As() const;
153
154 /// @brief Returns value of *this converted to T or T(args) if
155 /// this->IsMissing().
156 /// @throw Anything derived from std::exception.
157 template <typename T, typename First, typename... Rest>
158 T As(First&& default_arg, Rest&&... more_default_args) const;
159
160 /// @brief Returns value of *this converted to T or T() if this->IsMissing().
161 /// @throw Anything derived from std::exception.
162 /// @note Use as `value.As<T>({})`
163 template <typename T>
164 T As(DefaultConstructed) const;
165
166 /// @brief Returns true if *this holds a `key`.
167 bool HasMember(std::string_view key) const;
168
169 /// @brief Returns full path to this value.
170 std::string GetPath() const;
171
172 /// @brief Returns 0-based column number of this `Value` in the original
173 /// document. Returns `-1` if `this->IsMissing()`. If `Value` was created
174 /// using formats::yaml::ValueBuilder, returns `0`.
175 /// @note This method available **only** for formats::yaml::Value.
176 int GetColumn() const;
177
178 /// @brief Returns 0-based line number of this `Value` in the original
179 /// document. Returns `-1` if `this->IsMissing()`. If `Value` was created
180 /// using formats::yaml::ValueBuilder, returns `0`.
181 /// @note This method available **only** for formats::yaml::Value.
182 int GetLine() const;
183
184 /// @brief Returns new value that is an exact copy if the existing one
185 /// but references different memory (a deep copy of a *this). The returned
186 /// value is a root value with path '/'.
187 /// @throws MemberMissingException id `this->IsMissing()`.
188 Value Clone() const;
189
190 /// @throw MemberMissingException if `this->IsMissing()`.
191 void CheckNotMissing() const;
192
193 /// @throw MemberMissingException if `*this` is not an array.
194 void CheckArray() const;
195
196 /// @throw MemberMissingException if `*this` is not an array or Null.
197 void CheckArrayOrNull() const;
198
199 /// @throw TypeMismatchException if `*this` is not a map or Null.
200 void CheckObjectOrNull() const;
201
202 /// @throw TypeMismatchException if `*this` is not a map.
203 void CheckObject() const;
204
205 /// @throw TypeMismatchException if `*this` is not convertible to std::string.
206 void CheckString() const;
207
208 /// @throw TypeMismatchException if `*this` is not a map, array or Null.
210
211 /// @throw TypeMismatchException if `*this` is not a map, array or Null;
212 /// `OutOfBoundsException` if `index >= this->GetSize()`.
213 void CheckInBounds(std::size_t index) const;
214
215 /// @brief Returns true if *this is a first (root) value.
216 bool IsRoot() const noexcept;
217
218 /// @brief Returns true if `*this` and `other` reference the value by the same
219 /// pointer.
220 bool DebugIsReferencingSameMemory(const Value& other) const;
221
222 private:
223 class EmplaceEnabler {};
224
225 public:
226 /// @cond
227 Value(EmplaceEnabler, const YAML::Node& value,
228 const formats::yaml::Path& path, std::string_view key);
229
230 Value(EmplaceEnabler, const YAML::Node& value,
231 const formats::yaml::Path& path, size_t index);
232 /// @endcond
233
234 private:
235 Value(const YAML::Node& root) noexcept;
236
237 static Value MakeNonRoot(const YAML::Node& value,
238 const formats::yaml::Path& path,
239 std::string_view key);
240 static Value MakeNonRoot(const YAML::Node& val,
241 const formats::yaml::Path& path, size_t index);
242
243 const YAML::Node& GetNative() const;
244 YAML::Node& GetNative();
245 int GetExtendedType() const;
246
247 template <class T>
248 bool IsConvertible() const;
249
250 template <class T>
251 T ValueAs() const;
252
253 static constexpr std::size_t kNativeNodeSize = 64;
254 static constexpr std::size_t kNativeAlignment = alignof(void*);
255
256 utils::FastPimpl<YAML::Node, kNativeNodeSize, kNativeAlignment> value_pimpl_;
257 formats::yaml::Path path_;
258
259 friend class Iterator<IterTraits>;
260 friend class ValueBuilder;
261
262 friend formats::yaml::Value FromString(const std::string&);
263 friend formats::yaml::Value FromStream(std::istream&);
264 friend void Serialize(const formats::yaml::Value&, std::ostream&);
265};
266
267template <typename T>
268T Value::As() const {
269 static_assert(formats::common::impl::kHasParse<Value, T>,
270 "There is no `Parse(const Value&, formats::parse::To<T>)` in "
271 "namespace of `T` or `formats::parse`. "
272 "Probably you forgot to include the "
273 "<userver/formats/parse/common_containers.hpp> or you "
274 "have not provided a `Parse` function overload.");
275
276 return Parse(*this, formats::parse::To<T>{});
277}
278
279template <>
280bool Value::As<bool>() const;
281
282template <>
283int64_t Value::As<int64_t>() const;
284
285template <>
286uint64_t Value::As<uint64_t>() const;
287
288template <>
289double Value::As<double>() const;
290
291template <>
292std::string Value::As<std::string>() const;
293
294template <typename T, typename First, typename... Rest>
295T Value::As(First&& default_arg, Rest&&... more_default_args) const {
296 if (IsMissing() || IsNull()) {
297 // intended raw ctor call, sometimes casts
298 // NOLINTNEXTLINE(google-readability-casting)
299 return T(std::forward<First>(default_arg),
300 std::forward<Rest>(more_default_args)...);
301 }
302 return As<T>();
303}
304
305template <typename T>
306T Value::As(Value::DefaultConstructed) const {
307 return (IsMissing() || IsNull()) ? T() : As<T>();
308}
309
310/// @brief Wrapper for handy python-like iteration over a map
311///
312/// @code
313/// for (const auto& [name, value]: Items(map)) ...
314/// @endcode
315using formats::common::Items;
316
317} // namespace formats::yaml
318
319/// Although we provide user defined literals, please beware that
320/// 'using namespace ABC' may contradict code style of your company.
321namespace formats::literals {
322
323yaml::Value operator"" _yaml(const char* str, std::size_t len);
324
325} // namespace formats::literals
326
327USERVER_NAMESPACE_END