1
\$\begingroup\$

Follow up to Adding Lambda to Path

Removed RegEx fro the class.

Changed the behavior slightly. The named matching section {name} now matches anything. But there must be at least one character between each named matching section.

PathMatcher.h

#ifndef THORSANVIL_NISSE_NISSEHTTP_PATH_MATCHER_H
#define THORSANVIL_NISSE_NISSEHTTP_PATH_MATCHER_H
#include "Util.h"
#include <map>
#include <vector>
#include <string>
#include <functional>
namespace ThorsAnvil::Nisse::HTTP
{
class Request;
class Response;
using Match = std::map<std::string, std::string>;
class PathMatcher
{
 using Action = std::function<void(Match const&, Request&, Response&)>;
 using NameList = std::vector<std::string>;
 using MatchList = std::vector<std::string>;
 struct MatchInfo
 {
 Method method;
 MatchList matchSections;
 NameList names;
 Action action;
 };
 std::vector<MatchInfo> paths;
 public:
 void addPath(Method method, std::string pathMatch, Action&& action);
 bool findMatch(std::string_view path, Request& request, Response& response);
 private:
 bool checkPathMatch(MatchInfo const& pathMatchInfo, std::string_view path, Request& request, Response& response);
};
}
#endif

PathMatcher.cpp

#include "PathMatcher.h"
#include "Request.h"
#include <ThorsLogging/ThorsLogging.h>
using namespace ThorsAnvil::Nisse::HTTP;
void PathMatcher::addPath(Method method, std::string pathMatch, Action&& action)
{
 MatchList matchSections;
 NameList names;
 std::size_t prefix = 0;
 std::size_t nameBeg = 0;
 std::size_t nameEnd = 0;
 std::size_t size = pathMatch.size();
 bool first = true;
 while (prefix != size)
 {
 nameBeg = std::min(size, pathMatch.find('{', prefix));
 nameEnd = std::min(size, pathMatch.find('}', nameBeg));
 if (!first && prefix == nameBeg) {
 ThorsLogAndThrow("ThorsAnvil::Nisse::HTPP::PathMatcher", "addPath", "Invalid 'pathMatch' string. Multiple name sections with no gap");
 }
 matchSections.emplace_back(pathMatch.substr(prefix, nameBeg - prefix));
 first = false;
 if (nameBeg == size) {
 break;
 }
 if (nameEnd == size) {
 ThorsLogAndThrow("ThorsAnvil::Nisse::HTPP::PathMatcher", "addPath", "Invalid 'pathMatch' string. Badly nested braces.");
 }
 if (nameBeg + 1 == nameEnd) {
 ThorsLogAndThrow("ThorsAnvil::Nisse::HTPP::PathMatcher", "addPath", "Invalid 'pathMatch' string. Name section with no name");
 }
 names.emplace_back(pathMatch.substr(nameBeg + 1, nameEnd - nameBeg - 1));
 prefix = nameEnd + 1;
 }
 if (nameBeg != size) {
 matchSections.emplace_back("");
 }
 paths.emplace_back(method, std::move(matchSections), std::move(names), std::move(action));
}
bool PathMatcher::checkPathMatch(MatchInfo const& pathMatchInfo, std::string_view path, Request& request, Response& response)
{
 if (pathMatchInfo.method != request.getMethod()) {
 return false;
 }
 Match result;
 std::string_view prefix = path.substr(0, pathMatchInfo.matchSections[0].size());
 path.remove_prefix(pathMatchInfo.matchSections[0].size());
 if (pathMatchInfo.matchSections[0] != prefix) {
 return false;
 }
 for (std::size_t loop = 1; loop < pathMatchInfo.matchSections.size(); ++loop)
 {
 auto find = pathMatchInfo.matchSections[loop] == "" ? path.size() : path.find(pathMatchInfo.matchSections[loop]);
 if (find == std::string::npos) {
 return false;
 }
 result.emplace(pathMatchInfo.names[loop - 1], path.substr(0, find));
 path.remove_prefix(find);
 path.remove_prefix(pathMatchInfo.matchSections[loop].size());
 }
 if (!path.empty()) {
 return false;
 }
 pathMatchInfo.action(result, request, response);
 return true;
}
bool PathMatcher::findMatch(std::string_view path, Request& request, Response& response)
{
 for (auto const& pathMatchInfo: paths)
 {
 if (checkPathMatch(pathMatchInfo, path, request, response)) {
 return true;
 }
 }
 return false;
}
asked Oct 25, 2024 at 6:15
\$\endgroup\$

0

Know someone who can answer? Share a link to this question via email, Twitter, or Facebook.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.