Files
retoor 3c4334cec9 feat: implement MoloApp plans 01-15 with integration
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.
2026-10-23 18:20:04 +02:00

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