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