Vulcan
Aerospace Engineering Utilities Built on Janus
Loading...
Searching...
No Matches
YamlNode.hpp
Go to the documentation of this file.
1
10
11#pragma once
12
13#include <janus/core/JanusTypes.hpp>
14#include <janus/math/Quaternion.hpp>
15#include <yaml-cpp/yaml.h>
16
17#include <cstdint>
18#include <optional>
19#include <string>
20#include <vector>
21
22namespace vulcan::io {
23
27class YamlError : public std::runtime_error {
28 public:
29 YamlError(const std::string &path, const std::string &msg)
30 : std::runtime_error("YAML error at '" + path + "': " + msg),
31 path_(path) {}
32
33 [[nodiscard]] const std::string &path() const { return path_; }
34
35 private:
36 std::string path_;
37};
38
47class YamlNode {
48 public:
49 // =========================================================================
50 // Construction
51 // =========================================================================
52
54 static YamlNode LoadFile(const std::string &path) {
55 try {
56 return YamlNode(YAML::LoadFile(path), path);
57 } catch (const YAML::Exception &e) {
58 throw YamlError(path, e.what());
59 }
60 }
61
63 static YamlNode Parse(const std::string &yaml_content) {
64 try {
65 return YamlNode(YAML::Load(yaml_content), "root");
66 } catch (const YAML::Exception &e) {
67 throw YamlError("root", e.what());
68 }
69 }
70
72 explicit YamlNode(YAML::Node node, std::string path = "root")
73 : node_(std::move(node)), path_(std::move(path)) {}
74
75 // =========================================================================
76 // Navigation
77 // =========================================================================
78
80 [[nodiscard]] YamlNode operator[](const std::string &key) const {
81 return YamlNode(node_[key], path_ + "." + key);
82 }
83
85 [[nodiscard]] YamlNode operator[](std::size_t index) const {
86 return YamlNode(node_[index],
87 path_ + "[" + std::to_string(index) + "]");
88 }
89
91 [[nodiscard]] bool Has(const std::string &key) const {
92 return node_[key].IsDefined();
93 }
94
96 [[nodiscard]] bool IsDefined() const { return node_.IsDefined(); }
97
99 [[nodiscard]] bool IsSequence() const { return node_.IsSequence(); }
100
102 [[nodiscard]] bool IsMap() const { return node_.IsMap(); }
103
105 [[nodiscard]] bool IsScalar() const { return node_.IsScalar(); }
106
108 [[nodiscard]] bool IsNull() const { return node_.IsNull(); }
109
111 [[nodiscard]] std::size_t Size() const { return node_.size(); }
112
114 [[nodiscard]] const std::string &Path() const { return path_; }
115
116 // =========================================================================
117 // Typed Accessors - Required (throws if missing)
118 // =========================================================================
119
121 template <typename T>
122 [[nodiscard]] T Require(const std::string &key) const {
123 auto child = (*this)[key];
124 if (!child.IsDefined()) {
125 throw YamlError(child.Path(), "required key not found");
126 }
127 return child.As<T>();
128 }
129
131 template <typename T> [[nodiscard]] T As() const;
132
133 // =========================================================================
134 // Typed Accessors - Optional (returns default if missing)
135 // =========================================================================
136
138 template <typename T>
139 [[nodiscard]] T Get(const std::string &key, const T &default_value) const {
140 auto child = (*this)[key];
141 if (!child.IsDefined() || child.IsNull()) {
142 return default_value;
143 }
144 return child.As<T>();
145 }
146
148 template <typename T>
149 [[nodiscard]] std::optional<T> GetOptional(const std::string &key) const {
150 auto child = (*this)[key];
151 if (!child.IsDefined() || child.IsNull()) {
152 return std::nullopt;
153 }
154 return child.As<T>();
155 }
156
157 // =========================================================================
158 // Iteration
159 // =========================================================================
160
162 template <typename Func> void ForEach(Func &&func) const {
163 if (!IsSequence()) {
164 throw YamlError(path_, "expected sequence for iteration");
165 }
166 for (std::size_t i = 0; i < Size(); ++i) {
167 func((*this)[i]);
168 }
169 }
170
172 template <typename Func> void ForEachEntry(Func &&func) const {
173 if (!IsMap()) {
174 throw YamlError(path_, "expected map for iteration");
175 }
176 for (const auto &pair : node_) {
177 std::string key = pair.first.as<std::string>();
178 func(key, YamlNode(pair.second, path_ + "." + key));
179 }
180 }
181
183 template <typename T> [[nodiscard]] std::vector<T> ToVector() const {
184 if (!IsSequence()) {
185 throw YamlError(path_, "expected sequence for ToVector");
186 }
187 std::vector<T> result;
188 result.reserve(Size());
189 for (std::size_t i = 0; i < Size(); ++i) {
190 result.push_back((*this)[i].As<T>());
191 }
192 return result;
193 }
194
195 // =========================================================================
196 // Raw Access
197 // =========================================================================
198
200 [[nodiscard]] const YAML::Node &Raw() const { return node_; }
201
202 private:
203 YAML::Node node_;
204 std::string path_;
205};
206
207// =============================================================================
208// Template Specializations for Common Types
209// =============================================================================
210
211namespace detail {
212
213template <typename T>
214T yaml_as(const YAML::Node &node, const std::string &path) {
215 try {
216 return node.as<T>();
217 } catch (const YAML::Exception &e) {
218 throw YamlError(path, e.what());
219 }
220}
221
222} // namespace detail
223
224// --- Scalars ---
226template <> inline double YamlNode::As<double>() const {
227 return detail::yaml_as<double>(node_, path_);
228}
229
231template <> inline float YamlNode::As<float>() const {
232 return detail::yaml_as<float>(node_, path_);
233}
234
236template <> inline int YamlNode::As<int>() const {
237 return detail::yaml_as<int>(node_, path_);
238}
239
241template <> inline int64_t YamlNode::As<int64_t>() const {
242 return detail::yaml_as<int64_t>(node_, path_);
243}
244
246template <> inline bool YamlNode::As<bool>() const {
247 return detail::yaml_as<bool>(node_, path_);
248}
249
251template <> inline std::string YamlNode::As<std::string>() const {
252 return detail::yaml_as<std::string>(node_, path_);
253}
254
255// --- Janus types (double specialization) ---
256
258template <>
259inline janus::Vec3<double> YamlNode::As<janus::Vec3<double>>() const {
260 if (!IsSequence() || Size() != 3) {
261 throw YamlError(path_, "expected sequence of 3 elements for Vec3");
262 }
263 return janus::Vec3<double>{(*this)[0].As<double>(), (*this)[1].As<double>(),
264 (*this)[2].As<double>()};
265}
266
268template <>
269inline janus::Quaternion<double>
271 if (!IsSequence() || Size() != 4) {
272 throw YamlError(
273 path_, "expected sequence of 4 elements for Quaternion [w,x,y,z]");
274 }
275 // Scalar-first convention: [w, x, y, z]
276 return janus::Quaternion<double>{
277 (*this)[0].As<double>(), (*this)[1].As<double>(),
278 (*this)[2].As<double>(), (*this)[3].As<double>()};
279}
280
282template <>
283inline janus::Mat3<double> YamlNode::As<janus::Mat3<double>>() const {
284 if (!IsSequence()) {
285 throw YamlError(path_, "expected sequence for Mat3");
286 }
287
288 janus::Mat3<double> m;
289
290 // Nested format: [[r00, r01, r02], [r10, r11, r12], [r20, r21, r22]]
291 if (Size() == 3 && (*this)[0].IsSequence()) {
292 for (int i = 0; i < 3; ++i) {
293 auto row = (*this)[static_cast<std::size_t>(i)];
294 if (row.Size() != 3) {
295 throw YamlError(path_, "Mat3 row " + std::to_string(i) +
296 " must have 3 elements");
297 }
298 for (int j = 0; j < 3; ++j) {
299 m(i, j) = row[static_cast<std::size_t>(j)].As<double>();
300 }
301 }
302 return m;
303 }
304
305 // Flat format: [r00, r01, r02, r10, r11, r12, r20, r21, r22] (row-major)
306 if (Size() == 9) {
307 for (int i = 0; i < 3; ++i) {
308 for (int j = 0; j < 3; ++j) {
309 m(i, j) =
310 (*this)[static_cast<std::size_t>(i * 3 + j)].As<double>();
311 }
312 }
313 return m;
314 }
315
316 throw YamlError(path_,
317 "Mat3 must be 3x3 nested array or 9-element flat array");
318}
319
320// --- Containers ---
321
323template <>
324inline std::vector<double> YamlNode::As<std::vector<double>>() const {
325 return ToVector<double>();
326}
327
329template <> inline std::vector<int> YamlNode::As<std::vector<int>>() const {
330 return ToVector<int>();
331}
332
334template <>
335inline std::vector<std::string> YamlNode::As<std::vector<std::string>>() const {
336 return ToVector<std::string>();
337}
338
339} // namespace vulcan::io
Exception for YAML parsing/access errors.
Definition YamlNode.hpp:27
YamlError(const std::string &path, const std::string &msg)
Definition YamlNode.hpp:29
const std::string & path() const
Definition YamlNode.hpp:33
static YamlNode LoadFile(const std::string &path)
Load from file.
Definition YamlNode.hpp:54
void ForEachEntry(Func &&func) const
Iterate over map entries: func(key, node).
Definition YamlNode.hpp:172
std::size_t Size() const
Get sequence size.
Definition YamlNode.hpp:111
std::vector< T > ToVector() const
Convert sequence to vector.
Definition YamlNode.hpp:183
const std::string & Path() const
Get current path (for error messages).
Definition YamlNode.hpp:114
bool IsNull() const
Check if node is null.
Definition YamlNode.hpp:108
bool IsDefined() const
Check if node is defined.
Definition YamlNode.hpp:96
static YamlNode Parse(const std::string &yaml_content)
Parse from string.
Definition YamlNode.hpp:63
const YAML::Node & Raw() const
Get underlying yaml-cpp node.
Definition YamlNode.hpp:200
YamlNode operator[](std::size_t index) const
Access child by index (sequence).
Definition YamlNode.hpp:85
bool IsSequence() const
Check if node is a sequence.
Definition YamlNode.hpp:99
std::optional< T > GetOptional(const std::string &key) const
Get optional value (returns nullopt if missing).
Definition YamlNode.hpp:149
T Require(const std::string &key) const
Get required value (throws YamlError if missing or wrong type).
Definition YamlNode.hpp:122
YamlNode(YAML::Node node, std::string path="root")
Wrap an existing yaml-cpp node.
Definition YamlNode.hpp:72
void ForEach(Func &&func) const
Iterate over sequence elements.
Definition YamlNode.hpp:162
bool IsMap() const
Check if node is a map.
Definition YamlNode.hpp:102
bool Has(const std::string &key) const
Check if key exists.
Definition YamlNode.hpp:91
T As() const
Get required value from current node.
T Get(const std::string &key, const T &default_value) const
Get optional value with default.
Definition YamlNode.hpp:139
YamlNode operator[](const std::string &key) const
Access child by key (map).
Definition YamlNode.hpp:80
bool IsScalar() const
Check if node is a scalar.
Definition YamlNode.hpp:105
Definition YamlNode.hpp:211
T yaml_as(const YAML::Node &node, const std::string &path)
Definition YamlNode.hpp:214
Definition CSVExport.hpp:20