Skip to content

Commit f6a2456

Browse files
authored
Merge pull request #53 from build-cpp/better-validation
Better TOML validation
2 parents a2f8d9e + c713606 commit f6a2456

File tree

1 file changed

+37
-20
lines changed

1 file changed

+37
-20
lines changed

src/project_parser.cpp

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -143,14 +143,21 @@ class TomlChecker {
143143
};
144144

145145
class TomlCheckerRoot {
146+
const TomlBasicValue &m_root;
146147
std::deque<TomlChecker> m_checkers;
148+
tsl::ordered_map<toml::key, bool> m_visisted;
147149
bool m_checked = false;
148150

149151
public:
150-
TomlCheckerRoot() = default;
152+
TomlCheckerRoot(const TomlBasicValue &root) : m_root(root) {}
151153
TomlCheckerRoot(const TomlCheckerRoot &) = delete;
152154
TomlCheckerRoot(TomlCheckerRoot &&) = delete;
153155

156+
bool contains(const toml::key &ky) {
157+
m_visisted[ky] = true;
158+
return m_root.contains(ky);
159+
}
160+
154161
TomlChecker &create(const TomlBasicValue &v) {
155162
m_checkers.emplace_back(v);
156163
return m_checkers.back();
@@ -161,7 +168,14 @@ class TomlCheckerRoot {
161168
return m_checkers.back();
162169
}
163170

164-
void check(const tsl::ordered_map<std::string, std::string> &conditions) {
171+
void check(const tsl::ordered_map<std::string, std::string> &conditions, bool check_root) {
172+
if (check_root) {
173+
for (const auto &itr : m_root.as_table()) {
174+
if (!m_visisted.contains(itr.first)) {
175+
throw std::runtime_error(format_key_error("Unknown key '" + itr.first + "'", itr.first, itr.second));
176+
}
177+
}
178+
}
165179
for (const auto &checker : m_checkers) {
166180
checker.check(conditions);
167181
}
@@ -171,13 +185,16 @@ class TomlCheckerRoot {
171185
Project::Project(const Project *parent, const std::string &path, bool build) {
172186
const auto toml_path = fs::path(path) / "cmake.toml";
173187
if (!fs::exists(toml_path)) {
174-
throw std::runtime_error("No cmake.toml was found!");
188+
throw std::runtime_error("File not found '" + toml_path.string() + "'");
175189
}
176190
const auto toml = toml::parse<toml::discard_comments, tsl::ordered_map, std::vector>(toml_path.string());
191+
if (toml.size() == 0) {
192+
throw std::runtime_error("Empty TOML '" + toml_path.string() + "'");
193+
}
177194

178-
TomlCheckerRoot checker;
195+
TomlCheckerRoot checker(toml);
179196

180-
if (toml.contains("cmake")) {
197+
if (checker.contains("cmake")) {
181198
auto &cmake = checker.create(toml, "cmake");
182199

183200
cmake.required("version", cmake_version);
@@ -209,7 +226,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
209226

210227
// Skip the rest of the parsing when building
211228
if (build) {
212-
checker.check(conditions);
229+
checker.check(conditions, false);
213230
return;
214231
}
215232

@@ -231,14 +248,14 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
231248
templates = parent->templates;
232249
}
233250

234-
if (toml.contains("conditions")) {
251+
if (checker.contains("conditions")) {
235252
auto conds = toml::find<decltype(conditions)>(toml, "conditions");
236253
for (const auto &cond : conds) {
237254
conditions[cond.first] = cond.second;
238255
}
239256
}
240257

241-
if (toml.contains("project")) {
258+
if (checker.contains("project")) {
242259
auto &project = checker.create(toml, "project");
243260
project.required("name", project_name);
244261
project.optional("version", project_version);
@@ -251,7 +268,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
251268
project.optional("subdirs", project_subdirs);
252269
}
253270

254-
if (toml.contains("subdir")) {
271+
if (checker.contains("subdir")) {
255272
const auto &subs = toml::find(toml, "subdir").as_table();
256273
for (const auto &itr : subs) {
257274
Subdir subdir;
@@ -268,7 +285,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
268285
}
269286
}
270287

271-
if (toml.contains("settings")) {
288+
if (checker.contains("settings")) {
272289
using set_map = tsl::ordered_map<std::string, TomlBasicValue>;
273290
const auto &sets = toml::find<set_map>(toml, "settings");
274291
for (const auto &itr : sets) {
@@ -297,7 +314,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
297314
}
298315
}
299316

300-
if (toml.contains("options")) {
317+
if (checker.contains("options")) {
301318
using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
302319
const auto &opts = toml::find<opts_map>(toml, "options");
303320
for (const auto &itr : opts) {
@@ -315,7 +332,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
315332
}
316333
}
317334

318-
if (toml.contains("find-package")) {
335+
if (checker.contains("find-package")) {
319336
using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
320337
const auto &pkgs = toml::find<pkg_map>(toml, "find-package");
321338
for (const auto &itr : pkgs) {
@@ -337,7 +354,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
337354
}
338355

339356
// TODO: perform checking here
340-
if (toml.contains("fetch-content")) {
357+
if (checker.contains("fetch-content")) {
341358
const auto &fc = toml::find(toml, "fetch-content").as_table();
342359
for (const auto &itr : fc) {
343360
Content content;
@@ -370,7 +387,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
370387
}
371388
}
372389

373-
if (toml.contains("bin")) {
390+
if (checker.contains("bin")) {
374391
throw std::runtime_error("[[bin]] has been renamed to [[target]]");
375392
}
376393

@@ -489,7 +506,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
489506
return target;
490507
};
491508

492-
if (toml.contains("template")) {
509+
if (checker.contains("template")) {
493510
const auto &ts = toml::find(toml, "template").as_table();
494511
for (const auto &itr : ts) {
495512
auto &t = checker.create(itr.second);
@@ -517,15 +534,15 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
517534
}
518535
}
519536

520-
if (toml.contains("target")) {
537+
if (checker.contains("target")) {
521538
const auto &ts = toml::find(toml, "target").as_table();
522539
for (const auto &itr : ts) {
523540
auto &t = checker.create(itr.second);
524541
targets.push_back(parse_target(itr.first, t, false));
525542
}
526543
}
527544

528-
if (toml.contains("test")) {
545+
if (checker.contains("test")) {
529546
const auto &ts = toml::find(toml, "test").as_array();
530547
for (const auto &value : ts) {
531548
auto &t = checker.create(value);
@@ -540,7 +557,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
540557
}
541558
}
542559

543-
if (toml.contains("install")) {
560+
if (checker.contains("install")) {
544561
const auto &is = toml::find(toml, "install").as_array();
545562
for (const auto &value : is) {
546563
auto &i = checker.create(value);
@@ -556,7 +573,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
556573
}
557574
}
558575

559-
if (toml.contains("vcpkg")) {
576+
if (checker.contains("vcpkg")) {
560577
auto &v = checker.create(toml, "vcpkg");
561578
v.optional("url", vcpkg.url);
562579
v.optional("version", vcpkg.version);
@@ -582,7 +599,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
582599
}
583600
}
584601

585-
checker.check(conditions);
602+
checker.check(conditions, true);
586603
}
587604

588605
bool is_root_path(const std::string &path) {

0 commit comments

Comments
 (0)