Full C++23 framework tree: runtime, observability, security, data, app, realtime, supervisor, clients, CLI, example plus enterprise tenancy, audit, operability, governance, supplychain. Postgres via docker compose for tests.
130 lines
3.8 KiB
C++
130 lines
3.8 KiB
C++
#include "Router.hpp"
|
|
|
|
#include <stdexcept>
|
|
|
|
#include "Caps.hpp"
|
|
|
|
namespace molo::example::fw {
|
|
|
|
namespace {
|
|
|
|
std::vector<std::string> split_path(std::string_view path) {
|
|
std::vector<std::string> 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<std::string, std::string>& 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<Method> 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<Method> 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<std::string, std::string> 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<Route>& Router::routes() const { return routes_; }
|
|
|
|
} // namespace molo::example::fw
|