@@ -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 }
@@ -178,9 +192,9 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
178192 throw std::runtime_error (" Empty TOML '" + toml_path.string () + " '" );
179193 }
180194
181- TomlCheckerRoot checker;
195+ TomlCheckerRoot checker (toml) ;
182196
183- if (toml .contains (" cmake" )) {
197+ if (checker .contains (" cmake" )) {
184198 auto &cmake = checker.create (toml, " cmake" );
185199
186200 cmake.required (" version" , cmake_version);
@@ -212,7 +226,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
212226
213227 // Skip the rest of the parsing when building
214228 if (build) {
215- checker.check (conditions);
229+ checker.check (conditions, false );
216230 return ;
217231 }
218232
@@ -234,14 +248,14 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
234248 templates = parent->templates ;
235249 }
236250
237- if (toml .contains (" conditions" )) {
251+ if (checker .contains (" conditions" )) {
238252 auto conds = toml::find<decltype (conditions)>(toml, " conditions" );
239253 for (const auto &cond : conds) {
240254 conditions[cond.first ] = cond.second ;
241255 }
242256 }
243257
244- if (toml .contains (" project" )) {
258+ if (checker .contains (" project" )) {
245259 auto &project = checker.create (toml, " project" );
246260 project.required (" name" , project_name);
247261 project.optional (" version" , project_version);
@@ -254,7 +268,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
254268 project.optional (" subdirs" , project_subdirs);
255269 }
256270
257- if (toml .contains (" subdir" )) {
271+ if (checker .contains (" subdir" )) {
258272 const auto &subs = toml::find (toml, " subdir" ).as_table ();
259273 for (const auto &itr : subs) {
260274 Subdir subdir;
@@ -271,7 +285,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
271285 }
272286 }
273287
274- if (toml .contains (" settings" )) {
288+ if (checker .contains (" settings" )) {
275289 using set_map = tsl::ordered_map<std::string, TomlBasicValue>;
276290 const auto &sets = toml::find<set_map>(toml, " settings" );
277291 for (const auto &itr : sets) {
@@ -300,7 +314,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
300314 }
301315 }
302316
303- if (toml .contains (" options" )) {
317+ if (checker .contains (" options" )) {
304318 using opts_map = tsl::ordered_map<std::string, TomlBasicValue>;
305319 const auto &opts = toml::find<opts_map>(toml, " options" );
306320 for (const auto &itr : opts) {
@@ -318,7 +332,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
318332 }
319333 }
320334
321- if (toml .contains (" find-package" )) {
335+ if (checker .contains (" find-package" )) {
322336 using pkg_map = tsl::ordered_map<std::string, TomlBasicValue>;
323337 const auto &pkgs = toml::find<pkg_map>(toml, " find-package" );
324338 for (const auto &itr : pkgs) {
@@ -340,7 +354,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
340354 }
341355
342356 // TODO: perform checking here
343- if (toml .contains (" fetch-content" )) {
357+ if (checker .contains (" fetch-content" )) {
344358 const auto &fc = toml::find (toml, " fetch-content" ).as_table ();
345359 for (const auto &itr : fc) {
346360 Content content;
@@ -373,7 +387,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
373387 }
374388 }
375389
376- if (toml .contains (" bin" )) {
390+ if (checker .contains (" bin" )) {
377391 throw std::runtime_error (" [[bin]] has been renamed to [[target]]" );
378392 }
379393
@@ -492,7 +506,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
492506 return target;
493507 };
494508
495- if (toml .contains (" template" )) {
509+ if (checker .contains (" template" )) {
496510 const auto &ts = toml::find (toml, " template" ).as_table ();
497511 for (const auto &itr : ts) {
498512 auto &t = checker.create (itr.second );
@@ -520,15 +534,15 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
520534 }
521535 }
522536
523- if (toml .contains (" target" )) {
537+ if (checker .contains (" target" )) {
524538 const auto &ts = toml::find (toml, " target" ).as_table ();
525539 for (const auto &itr : ts) {
526540 auto &t = checker.create (itr.second );
527541 targets.push_back (parse_target (itr.first , t, false ));
528542 }
529543 }
530544
531- if (toml .contains (" test" )) {
545+ if (checker .contains (" test" )) {
532546 const auto &ts = toml::find (toml, " test" ).as_array ();
533547 for (const auto &value : ts) {
534548 auto &t = checker.create (value);
@@ -543,7 +557,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
543557 }
544558 }
545559
546- if (toml .contains (" install" )) {
560+ if (checker .contains (" install" )) {
547561 const auto &is = toml::find (toml, " install" ).as_array ();
548562 for (const auto &value : is) {
549563 auto &i = checker.create (value);
@@ -559,7 +573,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
559573 }
560574 }
561575
562- if (toml .contains (" vcpkg" )) {
576+ if (checker .contains (" vcpkg" )) {
563577 auto &v = checker.create (toml, " vcpkg" );
564578 v.optional (" url" , vcpkg.url );
565579 v.optional (" version" , vcpkg.version );
@@ -585,7 +599,7 @@ Project::Project(const Project *parent, const std::string &path, bool build) {
585599 }
586600 }
587601
588- checker.check (conditions);
602+ checker.check (conditions, true );
589603}
590604
591605bool is_root_path (const std::string &path) {
0 commit comments