diff --git a/examples/close_match.cpp b/examples/close_match.cpp index 682cac89..5045a090 100644 --- a/examples/close_match.cpp +++ b/examples/close_match.cpp @@ -13,15 +13,38 @@ #include #include +int prefixMatch(const std::string& s1, const std::string& s2) +{ + if (s1.size() < s2.size()) + { + if (s2.compare(0, s1.size(), s1.c_str()) == 0) + { + return s2.size()-s1.size(); + } + else { + return std::string::npos; + } + } + else { + if (s1.compare(0, s2.size(), s2.c_str()) == 0) + { + return s1.size()-s2.size(); + } + else { + return std::string::npos; + } + } +} + // Levenshtein distance function code generated by chatgpt int levenshteinDistance(const std::string &s1, const std::string &s2) { size_t len1 = s1.size(), len2 = s2.size(); std::vector> dp(len1 + 1, std::vector(len2 + 1)); - for(size_t i = 0; i <= len1; ++i) - dp[i][0] = static_cast(i); - for(size_t j = 0; j <= len2; ++j) - dp[0][j] = static_cast(j); + for(size_t ii = 0; ii <= len1; ++ii) + dp[ii][0] = static_cast(ii); + for(size_t jj = 0; jj <= len2; ++jj) + dp[0][jj] = static_cast(jj); for(size_t i = 1; i <= len1; ++i) { for(size_t j = 1; j <= len2; ++j) { @@ -37,13 +60,22 @@ int levenshteinDistance(const std::string &s1, const std::string &s2) { return dp[len1][len2]; } +enum class MatchType:std::uint8_t {proximity,prefix}; + // Finds the closest string from a list (modified from chat gpt code) -std::pair findClosestMatch(const std::string &input, const std::vector &candidates) { +std::pair findClosestMatch(const std::string &input, const std::vector &candidates,MatchType match) { std::string closest; int minDistance = (std::numeric_limits::max)(); - + int distance=minDistance; for(const auto &candidate : candidates) { - int distance = levenshteinDistance(input, candidate); + if (match == MatchType::proximity) + { + distance = levenshteinDistance(input, candidate); + } + else + { + distance = prefixMatch(input, candidate); + } if(distance < minDistance) { minDistance = distance; closest = candidate; @@ -53,29 +85,16 @@ std::pair findClosestMatch(const std::string &input, const std return {closest, minDistance}; } -/** This example demonstrates the use of `prefix_command` on a subcommand -to capture all subsequent arguments along with an alias to make it appear as a regular options. +void addCloseMatchDetection(CLI::App* app, MatchType match) +{ + app->allow_extras(true); -All the values after the "sub" or "--sub" are available in the remaining() method. -*/ -int main(int argc, const char *argv[]) { - - int value{0}; - CLI::App app{"cose string App"}; - app.add_option("-v", value, "value"); - - app.add_subcommand("install", ""); - app.add_subcommand("upgrade", ""); - app.add_subcommand("remove", ""); - app.add_subcommand("test", ""); - app.allow_extras(true); - - app.parse_complete_callback([&app]() { - auto extras = app.remaining(); + app->parse_complete_callback([&app,match]() { + auto extras = app->remaining(); if(extras.empty()) { return; } - auto subs = app.get_subcommands(nullptr); + auto subs = app->get_subcommands(nullptr); std::vector list; for(const auto *sub : subs) { if(!sub->get_name().empty()) { @@ -88,13 +107,28 @@ int main(int argc, const char *argv[]) { } for(auto &extra : extras) { if(extra.front() != '-') { - auto closest = findClosestMatch(extra, list); + auto closest = findClosestMatch(extra, list,match); if(closest.second <= 3) { std::cout << "unmatched commands " << extra << ", closest match is " << closest.first << "\n"; } } } - }); + }); +} + +/** This example demonstrates the use of close match detection to detect invalid commands that are close matches to existing ones +*/ +int main(int argc, const char *argv[]) { + + int value{0}; + CLI::App app{"cose string App"}; + app.add_option("-v", value, "value"); + + app.add_subcommand("install", ""); + app.add_subcommand("upgrade", ""); + app.add_subcommand("remove", ""); + app.add_subcommand("test", ""); + addCloseMatchDetection(&app,MatchType::prefix); CLI11_PARSE(app, argc, argv); return 0; }