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