Commit 1b3a4b63c894682bb991480991dfce5e495f538a
Committed by
Henry Schreiner
1 parent
133c7d50
Combining parse functions for short and long
Showing
2 changed files
with
39 additions
and
108 deletions
include/CLI/App.hpp
| ... | ... | @@ -1251,11 +1251,11 @@ class App { |
| 1251 | 1251 | break; |
| 1252 | 1252 | case detail::Classifer::LONG: |
| 1253 | 1253 | // If already parsed a subcommand, don't accept options_ |
| 1254 | - _parse_long(args); | |
| 1254 | + _parse_arg(args, true); | |
| 1255 | 1255 | break; |
| 1256 | 1256 | case detail::Classifer::SHORT: |
| 1257 | 1257 | // If already parsed a subcommand, don't accept options_ |
| 1258 | - _parse_short(args); | |
| 1258 | + _parse_arg(args, false); | |
| 1259 | 1259 | break; |
| 1260 | 1260 | case detail::Classifer::NONE: |
| 1261 | 1261 | // Probably a positional or something for a parent (sub)command |
| ... | ... | @@ -1327,27 +1327,38 @@ class App { |
| 1327 | 1327 | throw HorribleError("Subcommand " + args.back() + " missing"); |
| 1328 | 1328 | } |
| 1329 | 1329 | |
| 1330 | - /// Parse a short argument, must be at the top of the list | |
| 1331 | - void _parse_short(std::vector<std::string> &args) { | |
| 1330 | + /// Parse a short (false) or long (true) argument, must be at the top of the list | |
| 1331 | + void _parse_arg(std::vector<std::string> &args, bool second_dash) { | |
| 1332 | + | |
| 1333 | + detail::Classifer current_type = second_dash ? detail::Classifer::LONG : detail::Classifer::SHORT; | |
| 1334 | + | |
| 1332 | 1335 | std::string current = args.back(); |
| 1333 | 1336 | |
| 1334 | 1337 | std::string name; |
| 1338 | + std::string value; | |
| 1335 | 1339 | std::string rest; |
| 1336 | - if(!detail::split_short(current, name, rest)) | |
| 1337 | - throw HorribleError("Short parsed but missing! You should not see this"); | |
| 1338 | 1340 | |
| 1339 | - auto op_ptr = std::find_if( | |
| 1340 | - std::begin(options_), std::end(options_), [name](const Option_p &opt) { return opt->check_sname(name); }); | |
| 1341 | + if(second_dash) { | |
| 1342 | + if(!detail::split_long(current, name, value)) | |
| 1343 | + throw HorribleError("Long parsed but missing (you should not see this):" + args.back()); | |
| 1344 | + } else { | |
| 1345 | + if(!detail::split_short(current, name, rest)) | |
| 1346 | + throw HorribleError("Short parsed but missing! You should not see this"); | |
| 1347 | + } | |
| 1348 | + | |
| 1349 | + auto op_ptr = std::find_if(std::begin(options_), std::end(options_), [name, second_dash](const Option_p &opt) { | |
| 1350 | + return second_dash ? opt->check_lname(name) : opt->check_sname(name); | |
| 1351 | + }); | |
| 1341 | 1352 | |
| 1342 | 1353 | // Option not found |
| 1343 | 1354 | if(op_ptr == std::end(options_)) { |
| 1344 | 1355 | // If a subcommand, try the master command |
| 1345 | 1356 | if(parent_ != nullptr && fallthrough_) |
| 1346 | - return parent_->_parse_short(args); | |
| 1357 | + return parent_->_parse_arg(args, second_dash); | |
| 1347 | 1358 | // Otherwise, add to missing |
| 1348 | 1359 | else { |
| 1349 | 1360 | args.pop_back(); |
| 1350 | - missing_.emplace_back(detail::Classifer::SHORT, current); | |
| 1361 | + missing_.emplace_back(current_type, current); | |
| 1351 | 1362 | return; |
| 1352 | 1363 | } |
| 1353 | 1364 | } |
| ... | ... | @@ -1359,7 +1370,13 @@ class App { |
| 1359 | 1370 | |
| 1360 | 1371 | int num = op->get_expected(); |
| 1361 | 1372 | |
| 1362 | - if(num == 0) { | |
| 1373 | + /// ONE /////////////////////////////////////////////////////////////// | |
| 1374 | + if(!value.empty()) { | |
| 1375 | + if(num != -1) | |
| 1376 | + num--; | |
| 1377 | + op->add_result(value); | |
| 1378 | + parse_order_.push_back(op.get()); | |
| 1379 | + } else if(num == 0) { | |
| 1363 | 1380 | op->add_result(""); |
| 1364 | 1381 | parse_order_.push_back(op.get()); |
| 1365 | 1382 | } else if(!rest.empty()) { |
| ... | ... | @@ -1392,9 +1409,9 @@ class App { |
| 1392 | 1409 | args.pop_back(); |
| 1393 | 1410 | collected++; |
| 1394 | 1411 | } |
| 1395 | - //if(collected < -num) | |
| 1412 | + // if(collected < -num) | |
| 1396 | 1413 | // throw ArgumentMismatch(op->single_name() + ": At least " + std::to_string(-num) + " required"); |
| 1397 | - | |
| 1414 | + | |
| 1398 | 1415 | } else { |
| 1399 | 1416 | while(num > 0 && !args.empty()) { |
| 1400 | 1417 | num--; |
| ... | ... | @@ -1415,85 +1432,6 @@ class App { |
| 1415 | 1432 | args.push_back(rest); |
| 1416 | 1433 | } |
| 1417 | 1434 | } |
| 1418 | - | |
| 1419 | - /// Parse a long argument, must be at the top of the list | |
| 1420 | - void _parse_long(std::vector<std::string> &args) { | |
| 1421 | - std::string current = args.back(); | |
| 1422 | - | |
| 1423 | - std::string name; | |
| 1424 | - std::string value; | |
| 1425 | - if(!detail::split_long(current, name, value)) | |
| 1426 | - throw HorribleError("Long parsed but missing (you should not see this):" + args.back()); | |
| 1427 | - | |
| 1428 | - auto op_ptr = std::find_if( | |
| 1429 | - std::begin(options_), std::end(options_), [name](const Option_p &v) { return v->check_lname(name); }); | |
| 1430 | - | |
| 1431 | - // Option not found | |
| 1432 | - if(op_ptr == std::end(options_)) { | |
| 1433 | - // If a subcommand, try the master command | |
| 1434 | - if(parent_ != nullptr && fallthrough_) | |
| 1435 | - return parent_->_parse_long(args); | |
| 1436 | - // Otherwise, add to missing | |
| 1437 | - else { | |
| 1438 | - args.pop_back(); | |
| 1439 | - missing_.emplace_back(detail::Classifer::LONG, current); | |
| 1440 | - return; | |
| 1441 | - } | |
| 1442 | - } | |
| 1443 | - | |
| 1444 | - args.pop_back(); | |
| 1445 | - | |
| 1446 | - // Get a reference to the pointer to make syntax bearable | |
| 1447 | - Option_p &op = *op_ptr; | |
| 1448 | - | |
| 1449 | - int num = op->get_expected(); | |
| 1450 | - | |
| 1451 | - if(!value.empty()) { | |
| 1452 | - if(num != -1) | |
| 1453 | - num--; | |
| 1454 | - op->add_result(value); | |
| 1455 | - parse_order_.push_back(op.get()); | |
| 1456 | - } else if(num == 0) { | |
| 1457 | - op->add_result(""); | |
| 1458 | - parse_order_.push_back(op.get()); | |
| 1459 | - } else if(num < 0) { | |
| 1460 | - // Unlimited vector parser | |
| 1461 | - int collected = 0; // Make sure we always eat the minimum | |
| 1462 | - while(!args.empty() && _recognize(args.back()) == detail::Classifer::NONE) { | |
| 1463 | - if(collected >= -num) { | |
| 1464 | - // We could break here for allow extras, but we don't | |
| 1465 | - | |
| 1466 | - // If any positionals remain, don't keep eating | |
| 1467 | - if(_count_remaining_positionals() > 0) | |
| 1468 | - break; | |
| 1469 | - | |
| 1470 | - // If there are any unlimited positionals, those also take priority | |
| 1471 | - if(std::any_of(std::begin(options_), std::end(options_), [](const Option_p &opt) { | |
| 1472 | - return opt->get_positional() && opt->get_expected() < 0; | |
| 1473 | - })) | |
| 1474 | - break; | |
| 1475 | - } | |
| 1476 | - op->add_result(args.back()); | |
| 1477 | - parse_order_.push_back(op.get()); | |
| 1478 | - args.pop_back(); | |
| 1479 | - collected++; | |
| 1480 | - } | |
| 1481 | - //if(collected < -num) | |
| 1482 | - // throw ArgumentMismatch(op->single_name() + ": At least " + std::to_string(-num) + " required"); | |
| 1483 | - } else { | |
| 1484 | - while(num > 0 && !args.empty()) { | |
| 1485 | - num--; | |
| 1486 | - op->add_result(args.back()); | |
| 1487 | - parse_order_.push_back(op.get()); | |
| 1488 | - args.pop_back(); | |
| 1489 | - } | |
| 1490 | - if(num > 0) { | |
| 1491 | - throw ArgumentMismatch(op->single_name() + ": " + std::to_string(num) + " required " + | |
| 1492 | - op->get_type_name() + " missing"); | |
| 1493 | - } | |
| 1494 | - } | |
| 1495 | - return; | |
| 1496 | - } | |
| 1497 | 1435 | }; |
| 1498 | 1436 | |
| 1499 | 1437 | namespace FailureMessage { |
| ... | ... | @@ -1519,16 +1457,9 @@ struct AppFriend { |
| 1519 | 1457 | |
| 1520 | 1458 | /// Wrap _parse_short, perfectly forward arguments and return |
| 1521 | 1459 | template <typename... Args> |
| 1522 | - static auto parse_short(App *app, Args &&... args) -> | |
| 1523 | - typename std::result_of<decltype (&App::_parse_short)(App, Args...)>::type { | |
| 1524 | - return app->_parse_short(std::forward<Args>(args)...); | |
| 1525 | - } | |
| 1526 | - | |
| 1527 | - /// Wrap _parse_long, perfectly forward arguments and return | |
| 1528 | - template <typename... Args> | |
| 1529 | - static auto parse_long(App *app, Args &&... args) -> | |
| 1530 | - typename std::result_of<decltype (&App::_parse_long)(App, Args...)>::type { | |
| 1531 | - return app->_parse_long(std::forward<Args>(args)...); | |
| 1460 | + static auto parse_arg(App *app, Args &&... args) -> | |
| 1461 | + typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type { | |
| 1462 | + return app->_parse_arg(std::forward<Args>(args)...); | |
| 1532 | 1463 | } |
| 1533 | 1464 | |
| 1534 | 1465 | /// Wrap _parse_subcommand, perfectly forward arguments and return | ... | ... |
tests/AppTest.cpp
| ... | ... | @@ -248,13 +248,13 @@ TEST_F(TApp, MissingValueMoreThan) { |
| 248 | 248 | std::vector<int> vals2; |
| 249 | 249 | app.add_option("-v", vals1)->expected(-2); |
| 250 | 250 | app.add_option("--vals", vals2)->expected(-2); |
| 251 | - | |
| 251 | + | |
| 252 | 252 | args = {"-v", "2"}; |
| 253 | 253 | EXPECT_THROW(run(), CLI::ArgumentMismatch); |
| 254 | - | |
| 254 | + | |
| 255 | 255 | app.reset(); |
| 256 | - | |
| 257 | - args = {"--vals","4"}; | |
| 256 | + | |
| 257 | + args = {"--vals", "4"}; | |
| 258 | 258 | EXPECT_THROW(run(), CLI::ArgumentMismatch); |
| 259 | 259 | } |
| 260 | 260 | |
| ... | ... | @@ -1173,14 +1173,14 @@ TEST_F(TApp, AllowExtrasOrder) { |
| 1173 | 1173 | TEST_F(TApp, CheckShortFail) { |
| 1174 | 1174 | args = {"--two"}; |
| 1175 | 1175 | |
| 1176 | - EXPECT_THROW(CLI::detail::AppFriend::parse_short(&app, args), CLI::HorribleError); | |
| 1176 | + EXPECT_THROW(CLI::detail::AppFriend::parse_arg(&app, args, false), CLI::HorribleError); | |
| 1177 | 1177 | } |
| 1178 | 1178 | |
| 1179 | 1179 | // Test horrible error |
| 1180 | 1180 | TEST_F(TApp, CheckLongFail) { |
| 1181 | 1181 | args = {"-t"}; |
| 1182 | 1182 | |
| 1183 | - EXPECT_THROW(CLI::detail::AppFriend::parse_long(&app, args), CLI::HorribleError); | |
| 1183 | + EXPECT_THROW(CLI::detail::AppFriend::parse_arg(&app, args, true), CLI::HorribleError); | |
| 1184 | 1184 | } |
| 1185 | 1185 | |
| 1186 | 1186 | // Test horrible error | ... | ... |