@@ -143,14 +143,21 @@ class TomlChecker {
143143};
144144
145145class 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 {
171185Project::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
588605bool is_root_path (const std::string &path) {
0 commit comments