0
\$\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;
}
\$\endgroup\$

0

Your Answer

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.