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 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