userver: userver/formats/yaml/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/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 YAML tag of this node.
187 /// If tag is not explicitly specified, its value depends on node value. For
188 /// explicitly specified tags, its value depends on used `TAG` directives and
189 /// node value. There is an implicit `TAG` derictive for `!!` with prefix
190 /// `tag:yaml.org,2002:`.
191 ///
192 /// For example:
193 /// - `""` if field is null, even when tag is specified
194 /// - `"!"` for quoted and block strings if tag is not specified
195 /// - `"?"` for other fields if tag is not specified
196 /// - `"tag:yaml.org,2002:str"` for `!!str` tag
197 /// - `"!!str"` for `!<!!str>` tag
198 /// - `"!custom_tag"` for `!custom_tag` tag, if no additional `TAG` directives
199 /// are specified
200 ///
201 /// For details see YAML specification.
202 /// @throws MemberMissingException if `this->IsMissing()`.
203 /// @note This method available **only** for formats::yaml::Value.
205
206 /// @brief Returns new value that is an exact copy if the existing one
207 /// but references different memory (a deep copy of a *this). The returned
208 /// value is a root value with path '/'.
209 /// @throws MemberMissingException if `this->IsMissing()`.
210 Value Clone() const;
211
212 /// @throw MemberMissingException if `this->IsMissing()`.
213 void CheckNotMissing() const;
214
215 /// @throw MemberMissingException if `*this` is not an array.
216 void CheckArray() const;
217
218 /// @throw MemberMissingException if `*this` is not an array or Null.
219 void CheckArrayOrNull() const;
220
221 /// @throw TypeMismatchException if `*this` is not a map or Null.
222 void CheckObjectOrNull() const;
223
224 /// @throw TypeMismatchException if `*this` is not a map.
225 void CheckObject() const;
226
227 /// @throw TypeMismatchException if `*this` is not convertible to std::string.
228 void CheckString() const;
229
230 /// @throw TypeMismatchException if `*this` is not a map, array or Null.
232
233 /// @throw TypeMismatchException if `*this` is not a map, array or Null;
234 /// `OutOfBoundsException` if `index >= this->GetSize()`.
235 void CheckInBounds(std::size_t index) const;
236
237 /// @brief Returns true if *this is a first (root) value.
238 bool IsRoot() const noexcept;
239
240 /// @brief Returns true if `*this` and `other` reference the value by the same
241 /// pointer.
242 bool DebugIsReferencingSameMemory(const Value& other) const;
243
244 private:
245 class EmplaceEnabler {};
246
247 public:
248 /// @cond
249 Value(EmplaceEnabler, const YAML::Node& value,
250 const formats::yaml::Path& path, std::string_view key);
251
252 Value(EmplaceEnabler, const YAML::Node& value,
253 const formats::yaml::Path& path, size_t index);
254 /// @endcond
255
256 private:
257 Value(const YAML::Node& root) noexcept;
258
259 static Value MakeNonRoot(const YAML::Node& value,
260 const formats::yaml::Path& path,
261 std::string_view key);
262 static Value MakeNonRoot(const YAML::Node& val,
263 const formats::yaml::Path& path, size_t index);
264
265 const YAML::Node& GetNative() const;
266 YAML::Node& GetNative();
267 int GetExtendedType() const;
268
269 template <class T>
270 bool IsConvertibleToArithmetic() const;
271
272 template <class T>
273 T ValueAsArithmetic() const;
274
275 static constexpr std::size_t kNativeNodeSize = 64;
276 static constexpr std::size_t kNativeAlignment = alignof(void*);
277
278 utils::FastPimpl<YAML::Node, kNativeNodeSize, kNativeAlignment> value_pimpl_;
279 formats::yaml::Path path_;
280
281 friend class Iterator<IterTraits>;
282 friend class ValueBuilder;
283
284 friend bool Parse(const Value& value, parse::To<bool>);
285 friend int64_t Parse(const Value& value, parse::To<int64_t>);
286 friend uint64_t Parse(const Value& value, parse::To<uint64_t>);
287 friend double Parse(const Value& value, parse::To<double>);
288 friend std::string Parse(const Value& value, parse::To<std::string>);
289
290 friend formats::yaml::Value FromString(const std::string&);
291 friend formats::yaml::Value FromStream(std::istream&);
292 friend void Serialize(const formats::yaml::Value&, std::ostream&);
293};
294
295template <typename T>
296auto Value::As() const {
297 static_assert(formats::common::impl::kHasParse<Value, T>,
298 "There is no `Parse(const Value&, formats::parse::To<T>)` in "
299 "namespace of `T` or `formats::parse`. "
300 "Probably you forgot to include the "
301 "<userver/formats/parse/common_containers.hpp> or you "
302 "have not provided a `Parse` function overload.");
303
304 return Parse(*this, formats::parse::To<T>{});
305}
306
307bool Parse(const Value& value, parse::To<bool>);
308
309int64_t Parse(const Value& value, parse::To<int64_t>);
310
311uint64_t Parse(const Value& value, parse::To<uint64_t>);
312
313double Parse(const Value& value, parse::To<double>);
314
315std::string Parse(const Value& value, parse::To<std::string>);
316
317template <typename T, typename First, typename... Rest>
318auto Value::As(First&& default_arg, Rest&&... more_default_args) const {
319 if (IsMissing() || IsNull()) {
320 // intended raw ctor call, sometimes casts
321 // NOLINTNEXTLINE(google-readability-casting)
322 return decltype(As<T>())(std::forward<First>(default_arg),
323 std::forward<Rest>(more_default_args)...);
324 }
325 return As<T>();
326}
327
328template <typename T>
329auto Value::As(Value::DefaultConstructed) const {
330 return (IsMissing() || IsNull()) ? decltype(As<T>())() : As<T>();
331}
332
333/// @brief Wrapper for handy python-like iteration over a map
334///
335/// @code
336/// for (const auto& [name, value]: Items(map)) ...
337/// @endcode
338using formats::common::Items;
339
340} // namespace formats::yaml
341
342/// Although we provide user defined literals, please beware that
343/// 'using namespace ABC' may contradict code style of your company.
344namespace formats::literals {
345
346yaml::Value operator"" _yaml(const char* str, std::size_t len);
347
348} // namespace formats::literals
349
350USERVER_NAMESPACE_END