PixelBullet  0.0.1
A C++ game engine
Loading...
Searching...
No Matches
node.h
1#pragma once
2
4
5#include <algorithm>
6#include <charconv>
7#include <cstdint>
8#include <map>
9#include <sstream>
10#include <string>
11#include <string_view>
12#include <type_traits>
13#include <vector>
14
15namespace YAML
16{
17class Node;
18}
19
20namespace pixelbullet
21{
23enum class NodeType : uint8_t
24{
25 Null,
26 Object,
27 Array,
28 String,
29 Boolean,
30 Integer,
31 Decimal
32};
33
34using NodeValue = std::string;
35using NodeProperty = std::pair<std::string, class Node>;
36using NodeProperties = std::vector<NodeProperty>;
37
44class Node
45{
46public:
50 struct Format
51 {
53 char newLine;
54 char space;
56
57 constexpr Format(int8_t s, char nl, char spc, bool inlineArr)
59 , newLine(nl)
60 , space(spc)
61 , inlineArrays(inlineArr)
62 {
63 }
64
65 std::string GetIndentation(int8_t indentLevel) const
66 {
67 return std::string(spacesPerIndent * indentLevel, ' ');
68 }
69
70 static const Format beautified;
71 static const Format minified;
72 };
73
74 Node() = default;
75 Node(const Node&) = default;
76 Node(Node&&) noexcept = default;
77 Node& operator=(const Node&) = default;
78 Node& operator=(Node&&) noexcept = default;
79 ~Node() = default;
80
81 [[nodiscard]] const NodeValue& GetValue() const noexcept;
82 void SetValue(const NodeValue& value);
83 void SetValue(NodeValue&& value);
84
85 [[nodiscard]] NodeType GetType() const noexcept;
86 void SetType(NodeType type);
87
88 void Clear();
89
90 [[nodiscard]] bool IsValid() const noexcept;
91
92 [[nodiscard]] const NodeProperties& GetProperties() const noexcept;
93 NodeProperties& GetProperties();
94
95 [[nodiscard]] bool HasProperty(const std::string& name) const;
96 [[nodiscard]] const Node* GetProperty(const std::string& name) const;
97 void SetProperty(const std::string& name, const Node& node);
98 bool RemoveProperty(const std::string& name);
99
100 // For object-style indexing
101 Node& operator[](const std::string& key);
102 const Node& operator[](const std::string& key) const;
103
104 // For array-style indexing
105 Node& operator[](size_t index);
106 const Node& operator[](size_t index) const;
107
108 // Convenience functions for type conversion.
109 template <typename T>
110 T As() const
111 {
112 T out{};
113 (*this) >> out;
114 return out;
115 }
116
117 template <typename T>
118 void Set(const T& in)
119 {
120 (*this) << in;
121 }
122
123 // Iterators to properties
124 auto begin()
125 {
126 return properties_.begin();
127 }
128 auto end()
129 {
130 return properties_.end();
131 }
132 auto begin() const
133 {
134 return properties_.begin();
135 }
136 auto end() const
137 {
138 return properties_.end();
139 }
140
141 // Optional: returns the number of properties (useful for arrays/objects)
142 [[nodiscard]] size_t Size() const noexcept
143 {
144 return properties_.size();
145 }
146
148 friend const Node& operator>>(const Node& node, Node& out)
149 {
150 out = node;
151 return node;
152 }
153
155 friend Node& operator<<(Node& node, const Node& in)
156 {
157 node = in;
158 return node;
159 }
160
161 friend const Node& operator>>(const Node& node, bool& b)
162 {
163 if (node.value_ == "true" || node.value_ == "1")
164 {
165 b = true;
166 }
167 else if (node.value_ == "false" || node.value_ == "0")
168 {
169 b = false;
170 }
171 else
172 {
173 ASSERT(false, "Invalid boolean value: {}", node.value_);
174 b = false;
175 }
176 return node;
177 }
178
179 friend Node& operator<<(Node& node, bool b)
180 {
181 node.SetValue(b ? "true" : "false");
182 node.SetType(NodeType::Boolean);
183 return node;
184 }
185
186 static bool ParseYAML(Node& node, std::string_view str);
187 static bool WriteYAML(const Node& node, std::ostream& stream, const Format& format = Format::minified);
188
189private:
190 void EnsureObjectStateForMutation();
191 static Node YAMLToNode(const YAML::Node& yaml);
192
193 NodeProperties properties_;
194 NodeValue value_;
195 NodeType type_ = NodeType::Null;
196};
197
198// --- Free Conversion Operators ---
199
200// Arithmetic conversion using C++20 requires clause.
201template <typename T>
202requires std::is_arithmetic_v<T> Node& operator<<(Node& node, const T& value)
203{
204 char buffer[64]{};
205 auto [ptr, ec] = std::to_chars(buffer, buffer + sizeof(buffer), value);
206 ASSERT(ec == std::errc(), "Conversion to string failed");
207 node.SetValue(std::string(buffer, ptr));
208 if constexpr (std::is_integral_v<T>)
209 {
210 node.SetType(NodeType::Integer);
211 }
212 else if constexpr (std::is_floating_point_v<T>)
213 {
214 node.SetType(NodeType::Decimal);
215 }
216 return node;
217}
218
219template <typename T>
220requires std::is_arithmetic_v<T>
221void operator>>(const Node& node, T& value)
222{
223 const std::string& s = node.GetValue();
224 auto result = std::from_chars(s.data(), s.data() + s.size(), value);
225 ASSERT(result.ec == std::errc(), "Conversion failed for value: {}", s);
226}
227
228inline Node& operator<<(Node& node, const std::string& value)
229{
230 node.SetValue(value);
231 node.SetType(NodeType::String);
232 return node;
233}
234
235inline const Node& operator>>(const Node& node, std::string& value)
236{
237 value = node.GetValue();
238 return node;
239}
240
241template <typename T>
242Node& operator<<(Node& node, const std::vector<T>& vec)
243{
244 node.SetType(NodeType::Array);
245 auto& props = node.GetProperties();
246 props.clear();
247 for (const T& item : vec)
248 {
249 Node child;
250 child << item;
251 props.emplace_back("", child);
252 }
253 return node;
254}
255
256template <typename T>
257void operator>>(const Node& node, std::vector<T>& vec)
258{
259 ASSERT(node.GetType() == NodeType::Array, "Node is not an array");
260 vec.clear();
261 for (const auto& [key, child] : node.GetProperties())
262 {
263 T item;
264 child >> item;
265 vec.push_back(std::move(item));
266 }
267}
268
269template <typename T>
270Node& operator<<(Node& node, const std::map<std::string, T>& mapData)
271{
272 node.SetType(NodeType::Object);
273 auto& props = node.GetProperties();
274 props.clear();
275 for (const auto& [key, value] : mapData)
276 {
277 Node child;
278 child << value;
279 props.emplace_back(key, child);
280 }
281 return node;
282}
283
284template <typename T>
285void operator>>(const Node& node, std::map<std::string, T>& mapData)
286{
287 ASSERT(node.GetType() == NodeType::Object, "Node is not an object");
288 mapData.clear();
289 for (const auto& [key, child] : node.GetProperties())
290 {
291 T item;
292 child >> item;
293 mapData.emplace(key, std::move(item));
294 }
295}
296
297inline bool operator==(const Node& lhs, const Node& rhs)
298{
299 return lhs.GetType() == rhs.GetType() && lhs.GetValue() == rhs.GetValue() && lhs.GetProperties() == rhs.GetProperties();
300}
301
302inline bool operator!=(const Node& lhs, const Node& rhs)
303{
304 return !(lhs == rhs);
305}
306
307inline bool operator<(const Node& lhs, const Node& rhs)
308{
309 if (lhs.GetType() != rhs.GetType())
310 {
311 return lhs.GetType() < rhs.GetType();
312 }
313 if (lhs.GetValue() != rhs.GetValue())
314 {
315 return lhs.GetValue() < rhs.GetValue();
316 }
317 return lhs.GetProperties() < rhs.GetProperties();
318}
319
320// Enum conversions – now enabled only for enums.
321template <typename T>
322requires std::is_enum_v<T>
323const Node& operator>>(const Node& node, T& object)
324{
325 using Underlying = std::underlying_type_t<T>;
326 Underlying value{};
327 node >> value;
328 object = static_cast<T>(value);
329 return node;
330}
331
332template <typename T>
333requires std::is_enum_v<T> Node& operator<<(Node& node, T object)
334{
335 return node << static_cast<std::underlying_type_t<T>>(object);
336}
337
338inline const Node& operator>>(const Node& node, std::pair<std::string, std::string>& p)
339{
340 ASSERT(node.GetType() == NodeType::Object, "Expected object type for a pair");
341 const auto& props = node.GetProperties();
342 ASSERT(props.size() == 1, "Expected exactly one property for a pair");
343 p.first = props[0].first;
344 props[0].second >> p.second;
345 return node;
346}
347
348inline Node& operator<<(Node& node, const std::pair<std::string, std::string>& p)
349{
350 node.SetType(NodeType::Object);
351 auto& props = node.GetProperties();
352 props.clear();
353 Node valueNode;
354 valueNode << p.second;
355 props.emplace_back(p.first, valueNode);
356 return node;
357}
358} // namespace pixelbullet
Provides assertion and panic mechanisms with optional custom formatting.
#define ASSERT(condition,...)
Asserts that a condition is true.
Definition assert.h:163
Represents a hierarchical node capable of storing various data types and supporting YAML serializatio...
Definition node.h:45
friend Node & operator<<(Node &node, const Node &in)
Copy content from one node to another.
Definition node.h:155
friend const Node & operator>>(const Node &node, Node &out)
Copy the node's content.
Definition node.h:148
Formatting options for YAML serialization.
Definition node.h:51
char newLine
New line character.
Definition node.h:53
char space
Space character.
Definition node.h:54
bool inlineArrays
Flag to inline arrays.
Definition node.h:55
static const Format minified
Predefined minified format.
Definition node.h:71
int8_t spacesPerIndent
Number of spaces per indent level.
Definition node.h:52
static const Format beautified
Predefined beautified format.
Definition node.h:70