|
7 | 7 | #include <fstream>
|
8 | 8 | #include <iostream>
|
9 | 9 | #include <regex>
|
| 10 | +#include <set> |
10 | 11 | #include <sstream>
|
11 | 12 | #include <stdexcept>
|
12 | 13 |
|
@@ -86,6 +87,9 @@ void SchemaLoader::validateSchema()
|
86 | 87 | // Handle nested input types by fully declaring the dependencies first.
|
87 | 88 | reorderInputTypeDependencies();
|
88 | 89 |
|
| 90 | + // Validate the interface dependencies and that all of the interface fields are implemented. |
| 91 | + validateImplementedInterfaces(); |
| 92 | + |
89 | 93 | for (auto& entry : _interfaceTypes)
|
90 | 94 | {
|
91 | 95 | fixupOutputFieldList(entry.fields, std::nullopt, std::nullopt);
|
@@ -186,30 +190,6 @@ void SchemaLoader::validateSchema()
|
186 | 190 | fixupOutputFieldList(entry.fields, interfaceFields, accessor);
|
187 | 191 | }
|
188 | 192 |
|
189 |
| - // Validate the interfaces implemented by the object types. |
190 |
| - for (const auto& entry : _objectTypes) |
191 |
| - { |
192 |
| - for (const auto& interfaceName : entry.interfaces) |
193 |
| - { |
194 |
| - if (_interfaceNames.find(interfaceName) == _interfaceNames.cend()) |
195 |
| - { |
196 |
| - std::ostringstream error; |
197 |
| - auto itrPosition = _typePositions.find(entry.type); |
198 |
| - |
199 |
| - error << "Unknown interface: " << interfaceName |
200 |
| - << " implemented by: " << entry.type; |
201 |
| - |
202 |
| - if (itrPosition != _typePositions.cend()) |
203 |
| - { |
204 |
| - error << " line: " << itrPosition->second.line |
205 |
| - << " column: " << itrPosition->second.column; |
206 |
| - } |
207 |
| - |
208 |
| - throw std::runtime_error(error.str()); |
209 |
| - } |
210 |
| - } |
211 |
| - } |
212 |
| - |
213 | 193 | // Validate the objects that are possible types for unions and add the unions to
|
214 | 194 | // the list of matching types for the objects.
|
215 | 195 | for (const auto& entry : _unionTypes)
|
@@ -433,6 +413,29 @@ void SchemaLoader::reorderInputTypeDependencies()
|
433 | 413 | }
|
434 | 414 | }
|
435 | 415 |
|
| 416 | +void SchemaLoader::validateImplementedInterfaces() const |
| 417 | +{ |
| 418 | + for (const auto& interfaceType : _interfaceTypes) |
| 419 | + { |
| 420 | + validateTransitiveInterfaces(interfaceType.type, interfaceType.interfaces); |
| 421 | + |
| 422 | + for (auto interfaceName : interfaceType.interfaces) |
| 423 | + { |
| 424 | + validateInterfaceFields(interfaceType.type, interfaceType.fields, interfaceName); |
| 425 | + } |
| 426 | + } |
| 427 | + |
| 428 | + for (const auto& objectType : _objectTypes) |
| 429 | + { |
| 430 | + validateTransitiveInterfaces(objectType.type, objectType.interfaces); |
| 431 | + |
| 432 | + for (auto interfaceName : objectType.interfaces) |
| 433 | + { |
| 434 | + validateInterfaceFields(objectType.type, objectType.fields, interfaceName); |
| 435 | + } |
| 436 | + } |
| 437 | +} |
| 438 | + |
436 | 439 | void SchemaLoader::visitDefinition(const peg::ast_node& definition)
|
437 | 440 | {
|
438 | 441 | if (definition.is_type<peg::schema_definition>())
|
@@ -1148,6 +1151,154 @@ void SchemaLoader::blockReservedName(
|
1148 | 1151 | }
|
1149 | 1152 | }
|
1150 | 1153 |
|
| 1154 | +void SchemaLoader::validateInterfaceFields(std::string_view typeName, |
| 1155 | + const OutputFieldList& typeFields, std::string_view interfaceName) const |
| 1156 | +{ |
| 1157 | + const auto itrType = _interfaceNames.find(interfaceName); |
| 1158 | + |
| 1159 | + if (itrType == _interfaceNames.cend()) |
| 1160 | + { |
| 1161 | + std::ostringstream error; |
| 1162 | + const auto itrPosition = _typePositions.find(typeName); |
| 1163 | + |
| 1164 | + error << "Unknown interface: " << interfaceName << " implemented by: " << typeName; |
| 1165 | + |
| 1166 | + if (itrPosition != _typePositions.cend()) |
| 1167 | + { |
| 1168 | + error << " line: " << itrPosition->second.line |
| 1169 | + << " column: " << itrPosition->second.column; |
| 1170 | + } |
| 1171 | + |
| 1172 | + throw std::runtime_error(error.str()); |
| 1173 | + } |
| 1174 | + |
| 1175 | + const auto& interfaceType = _interfaceTypes[itrType->second]; |
| 1176 | + std::set<std::string_view> unimplemented; |
| 1177 | + |
| 1178 | + for (const auto& entry : interfaceType.fields) |
| 1179 | + { |
| 1180 | + unimplemented.insert(entry.name); |
| 1181 | + } |
| 1182 | + |
| 1183 | + for (const auto& entry : typeFields) |
| 1184 | + { |
| 1185 | + unimplemented.erase(entry.name); |
| 1186 | + } |
| 1187 | + |
| 1188 | + if (!unimplemented.empty()) |
| 1189 | + { |
| 1190 | + std::ostringstream error; |
| 1191 | + const auto itrPosition = _typePositions.find(typeName); |
| 1192 | + |
| 1193 | + error << "Missing interface fields type: " << typeName |
| 1194 | + << " interface: " << interfaceType.type; |
| 1195 | + |
| 1196 | + if (itrPosition != _typePositions.cend()) |
| 1197 | + { |
| 1198 | + error << " line: " << itrPosition->second.line |
| 1199 | + << " column: " << itrPosition->second.column; |
| 1200 | + } |
| 1201 | + |
| 1202 | + for (auto fieldName : unimplemented) |
| 1203 | + { |
| 1204 | + error << " field: " << fieldName; |
| 1205 | + } |
| 1206 | + |
| 1207 | + throw std::runtime_error(error.str()); |
| 1208 | + } |
| 1209 | +} |
| 1210 | + |
| 1211 | +void SchemaLoader::validateTransitiveInterfaces( |
| 1212 | + std::string_view typeName, const std::vector<std::string_view>& interfaces) const |
| 1213 | +{ |
| 1214 | + std::set<std::string_view> unimplemented; |
| 1215 | + |
| 1216 | + for (auto entry : interfaces) |
| 1217 | + { |
| 1218 | + const auto itrType = _interfaceNames.find(entry); |
| 1219 | + |
| 1220 | + if (itrType == _interfaceNames.cend()) |
| 1221 | + { |
| 1222 | + std::ostringstream error; |
| 1223 | + const auto itrPosition = _typePositions.find(typeName); |
| 1224 | + |
| 1225 | + error << "Unknown interface: " << entry << " implemented by: " << typeName; |
| 1226 | + |
| 1227 | + if (itrPosition != _typePositions.cend()) |
| 1228 | + { |
| 1229 | + error << " line: " << itrPosition->second.line |
| 1230 | + << " column: " << itrPosition->second.column; |
| 1231 | + } |
| 1232 | + |
| 1233 | + throw std::runtime_error(error.str()); |
| 1234 | + } |
| 1235 | + |
| 1236 | + if (typeName == entry || !unimplemented.insert(entry).second) |
| 1237 | + { |
| 1238 | + std::ostringstream error; |
| 1239 | + const auto itrPosition = _typePositions.find(typeName); |
| 1240 | + |
| 1241 | + error << "Interface cycle interface: " << entry << " implemented by: " << typeName; |
| 1242 | + |
| 1243 | + if (itrPosition != _typePositions.cend()) |
| 1244 | + { |
| 1245 | + error << " line: " << itrPosition->second.line |
| 1246 | + << " column: " << itrPosition->second.column; |
| 1247 | + } |
| 1248 | + |
| 1249 | + throw std::runtime_error(error.str()); |
| 1250 | + } |
| 1251 | + |
| 1252 | + const auto& interfaceType = _interfaceTypes[itrType->second]; |
| 1253 | + |
| 1254 | + for (auto interfaceName : interfaceType.interfaces) |
| 1255 | + { |
| 1256 | + if (!unimplemented.insert(interfaceName).second) |
| 1257 | + { |
| 1258 | + std::ostringstream error; |
| 1259 | + const auto itrPosition = _typePositions.find(typeName); |
| 1260 | + |
| 1261 | + error << "Interface cycle interface: " << interfaceName |
| 1262 | + << " implemented by: " << typeName; |
| 1263 | + |
| 1264 | + if (itrPosition != _typePositions.cend()) |
| 1265 | + { |
| 1266 | + error << " line: " << itrPosition->second.line |
| 1267 | + << " column: " << itrPosition->second.column; |
| 1268 | + } |
| 1269 | + |
| 1270 | + throw std::runtime_error(error.str()); |
| 1271 | + } |
| 1272 | + } |
| 1273 | + } |
| 1274 | + |
| 1275 | + for (auto entry : interfaces) |
| 1276 | + { |
| 1277 | + unimplemented.erase(entry); |
| 1278 | + } |
| 1279 | + |
| 1280 | + if (!unimplemented.empty()) |
| 1281 | + { |
| 1282 | + std::ostringstream error; |
| 1283 | + const auto itrPosition = _typePositions.find(typeName); |
| 1284 | + |
| 1285 | + error << "Missing transitive interface type: " << typeName; |
| 1286 | + |
| 1287 | + if (itrPosition != _typePositions.cend()) |
| 1288 | + { |
| 1289 | + error << " line: " << itrPosition->second.line |
| 1290 | + << " column: " << itrPosition->second.column; |
| 1291 | + } |
| 1292 | + |
| 1293 | + for (auto interfaceName : unimplemented) |
| 1294 | + { |
| 1295 | + error << " interface: " << interfaceName; |
| 1296 | + } |
| 1297 | + |
| 1298 | + throw std::runtime_error(error.str()); |
| 1299 | + } |
| 1300 | +} |
| 1301 | + |
1151 | 1302 | OutputFieldList SchemaLoader::getOutputFields(const peg::ast_node::children_t& fields)
|
1152 | 1303 | {
|
1153 | 1304 | OutputFieldList outputFields;
|
|
0 commit comments