Vulcan
Aerospace Engineering Utilities Built on Janus
Loading...
Searching...
No Matches
YamlEnv.hpp
Go to the documentation of this file.
1
10
11#pragma once
12
15
16#include <cstdlib>
17#include <optional>
18#include <string>
19
20namespace vulcan::io {
21
25class EnvVarError : public YamlError {
26 public:
27 explicit EnvVarError(const std::string &var_name)
28 : YamlError("env", "undefined environment variable: " + var_name),
29 var_name_(var_name) {}
30
31 [[nodiscard]] const std::string &var_name() const { return var_name_; }
32
33 private:
34 std::string var_name_;
35};
36
40class YamlEnv {
41 public:
42 // =========================================================================
43 // String Expansion
44 // =========================================================================
45
54 static std::string Expand(const std::string &value, bool strict = true) {
55 std::string result;
56 result.reserve(value.size());
57
58 std::size_t i = 0;
59 while (i < value.size()) {
60 // Check for escape sequence $${ -> ${
61 if (i + 2 < value.size() && value[i] == '$' &&
62 value[i + 1] == '$' && value[i + 2] == '{') {
63 result += "${";
64 i += 3;
65 continue;
66 }
67
68 // Check for variable ${VAR} or ${VAR:default}
69 if (i + 1 < value.size() && value[i] == '$' &&
70 value[i + 1] == '{') {
71 std::size_t start = i + 2;
72 std::size_t end = value.find('}', start);
73 if (end == std::string::npos) {
74 // No closing brace, treat as literal
75 result += value[i];
76 ++i;
77 continue;
78 }
79
80 std::string content = value.substr(start, end - start);
81 std::string var_name;
82 std::string default_value;
83 bool has_default = false;
84
85 // Split on first colon for default value
86 std::size_t colon_pos = content.find(':');
87 if (colon_pos != std::string::npos) {
88 var_name = content.substr(0, colon_pos);
89 default_value = content.substr(colon_pos + 1);
90 has_default = true;
91 } else {
92 var_name = content;
93 }
94
95 auto env_value = GetEnv(var_name);
96 if (env_value.has_value()) {
97 result += env_value.value();
98 } else if (has_default) {
99 result += default_value;
100 } else if (strict) {
101 throw EnvVarError(var_name);
102 }
103 // If not strict and no default, variable is removed (empty)
104
105 i = end + 1;
106 continue;
107 }
108
109 // Regular character
110 result += value[i];
111 ++i;
112 }
113
114 return result;
115 }
116
120 static bool ContainsEnvVars(const std::string &value) {
121 // Look for ${ followed eventually by }
122 std::size_t pos = 0;
123 while ((pos = value.find("${", pos)) != std::string::npos) {
124 std::size_t end = value.find('}', pos + 2);
125 if (end != std::string::npos) {
126 return true;
127 }
128 pos += 2;
129 }
130 return false;
131 }
132
133 // =========================================================================
134 // Node Expansion (Recursive)
135 // =========================================================================
136
143 static YamlNode ExpandAll(const YamlNode &node) {
144 YAML::Node expanded = ExpandNodeRecursive(node.Raw());
145 return YamlNode(expanded, node.Path());
146 }
147
148 // =========================================================================
149 // File Loading with Expansion
150 // =========================================================================
151
159 static YamlNode LoadFileWithEnv(const std::string &path) {
160 auto node = YamlNode::LoadFile(path);
161 return ExpandAll(node);
162 }
163
172 static YamlNode LoadWithIncludesAndEnv(const std::string &path) {
173 std::string expanded_path = Expand(path, true);
174 auto node = YamlFile::LoadWithIncludes(expanded_path);
175 return ExpandAll(node);
176 }
177
181 static YamlNode LoadFromEnvPath(const std::string &path_with_vars) {
182 std::string expanded_path = Expand(path_with_vars, true);
183 return YamlNode::LoadFile(expanded_path);
184 }
185
186 // =========================================================================
187 // Environment Access
188 // =========================================================================
189
196 static std::optional<std::string> GetEnv(const std::string &name) {
197 const char *value = std::getenv(name.c_str());
198 if (value == nullptr) {
199 return std::nullopt;
200 }
201 return std::string(value);
202 }
203
207 static std::string GetEnv(const std::string &name,
208 const std::string &default_value) {
209 return GetEnv(name).value_or(default_value);
210 }
211
212 private:
214 static YAML::Node ExpandNodeRecursive(const YAML::Node &node) {
215 if (node.IsScalar()) {
216 std::string value = node.as<std::string>();
217 if (ContainsEnvVars(value)) {
218 return YAML::Node(Expand(value, true));
219 }
220 return node;
221 }
222
223 if (node.IsSequence()) {
224 YAML::Node result;
225 for (std::size_t i = 0; i < node.size(); ++i) {
226 result.push_back(ExpandNodeRecursive(node[i]));
227 }
228 return result;
229 }
230
231 if (node.IsMap()) {
232 YAML::Node result;
233 for (const auto &pair : node) {
234 std::string key = pair.first.as<std::string>();
235 // Expand envvars in keys too
236 if (ContainsEnvVars(key)) {
237 key = Expand(key, true);
238 }
239 result[key] = ExpandNodeRecursive(pair.second);
240 }
241 return result;
242 }
243
244 return node;
245 }
246};
247
248} // namespace vulcan::io
YAML file utilities for includes, merging, and discovery.
Type-safe YAML wrapper with path-aware error messages.
Exception for undefined environment variables.
Definition YamlEnv.hpp:25
EnvVarError(const std::string &var_name)
Definition YamlEnv.hpp:27
const std::string & var_name() const
Definition YamlEnv.hpp:31
Environment variable expansion utilities.
Definition YamlEnv.hpp:40
static YamlNode LoadFromEnvPath(const std::string &path_with_vars)
Expand envvars in path, then load the file.
Definition YamlEnv.hpp:181
static std::string Expand(const std::string &value, bool strict=true)
Expand environment variables in a string.
Definition YamlEnv.hpp:54
static YamlNode LoadFileWithEnv(const std::string &path)
Load YAML file with environment variable expansion.
Definition YamlEnv.hpp:159
static std::optional< std::string > GetEnv(const std::string &name)
Get environment variable value.
Definition YamlEnv.hpp:196
static bool ContainsEnvVars(const std::string &value)
Check if string contains environment variable references.
Definition YamlEnv.hpp:120
static YamlNode LoadWithIncludesAndEnv(const std::string &path)
Load with includes AND environment expansion.
Definition YamlEnv.hpp:172
static YamlNode ExpandAll(const YamlNode &node)
Recursively expand all string values in a YAML tree.
Definition YamlEnv.hpp:143
static std::string GetEnv(const std::string &name, const std::string &default_value)
Get environment variable with default.
Definition YamlEnv.hpp:207
YamlError(const std::string &path, const std::string &msg)
Definition YamlNode.hpp:29
static YamlNode LoadWithIncludes(const std::string &path)
Load YAML from file with !include directive resolution.
Definition YamlFile.hpp:37
Wrapper around yaml-cpp node with ergonomic accessors.
Definition YamlNode.hpp:47
static YamlNode LoadFile(const std::string &path)
Load from file.
Definition YamlNode.hpp:54
const std::string & Path() const
Get current path (for error messages).
Definition YamlNode.hpp:114
const YAML::Node & Raw() const
Get underlying yaml-cpp node.
Definition YamlNode.hpp:200
Definition CSVExport.hpp:20