#include "Router.hpp" #include #include "Caps.hpp" namespace molo::example::fw { namespace { std::vector split_path(std::string_view path) { std::vector segs; std::string cur; for (char c : path) { if (c == '/') { if (!cur.empty()) { segs.push_back(cur); cur.clear(); } } else { cur.push_back(c); } } if (!cur.empty()) segs.push_back(cur); return segs; } } // namespace bool match_pattern(std::string_view pattern, std::string_view path, std::map& params) { auto want = split_path(pattern); auto got = split_path(path); if (want.size() != got.size()) return false; for (std::size_t i = 0; i < want.size(); ++i) { const std::string& w = want[i]; if (w.size() >= 2 && w.front() == '{' && w.back() == '}') { params[w.substr(1, w.size() - 2)] = url_decode(got[i]); } else if (w != got[i]) { return false; } } return true; } void Router::add(std::vector methods, std::string pattern, std::string name, Handler handler) { if (routes_.size() >= caps::kMaxRouteCount) throw std::length_error("route cap exceeded"); for (const auto& r : routes_) { if (r.name == name) throw std::runtime_error("duplicate route name: " + name); } routes_.push_back( Route{std::move(methods), std::move(pattern), std::move(name), std::move(handler)}); } void Router::get(std::string pattern, std::string name, Handler handler) { add({Method::Get}, std::move(pattern), std::move(name), std::move(handler)); } void Router::post(std::string pattern, std::string name, Handler handler) { add({Method::Post}, std::move(pattern), std::move(name), std::move(handler)); } void Router::put(std::string pattern, std::string name, Handler handler) { add({Method::Put}, std::move(pattern), std::move(name), std::move(handler)); } void Router::patch(std::string pattern, std::string name, Handler handler) { add({Method::Patch}, std::move(pattern), std::move(name), std::move(handler)); } void Router::del(std::string pattern, std::string name, Handler handler) { add({Method::Delete}, std::move(pattern), std::move(name), std::move(handler)); } void Router::options(std::string pattern, std::string name, Handler handler) { add({Method::Options}, std::move(pattern), std::move(name), std::move(handler)); } void Router::any(std::vector methods, std::string pattern, std::string name, Handler handler) { add(std::move(methods), std::move(pattern), std::move(name), std::move(handler)); } void Router::include_router(const std::string& prefix, const Router& other, const std::string& name_prefix) { for (const auto& r : other.routes_) { std::string pattern = prefix; if (!r.pattern.empty() && r.pattern != "/") { if (pattern.empty() || pattern.back() != '/') pattern += "/"; std::string sub = r.pattern; if (!sub.empty() && sub.front() == '/') sub.erase(sub.begin()); pattern += sub; } if (pattern.empty()) pattern = "/"; std::string name = name_prefix.empty() ? r.name : name_prefix + r.name; add(r.methods, pattern, name, r.handler); } } bool Router::match(Request& req, Handler& out) const { for (const auto& r : routes_) { bool method_ok = false; for (Method m : r.methods) { if (m == req.method) { method_ok = true; break; } } if (!method_ok) continue; std::map params; if (match_pattern(r.pattern, req.path, params)) { for (auto& [k, v] : params) req.path_params[k] = v; out = r.handler; return true; } } return false; } const std::vector& Router::routes() const { return routes_; } } // namespace molo::example::fw