aboutsummaryrefslogtreecommitdiffstats
path: root/routing.h
diff options
context:
space:
mode:
authoripknHama <ipknhama@gmail.com>2014-04-13 11:24:06 +0900
committeripknHama <ipknhama@gmail.com>2014-04-13 11:24:06 +0900
commit7ec586556e348725bff3919f4787a75d71c520fa (patch)
treeb9ca0d11bbe6637ab4b451e7d14702a4c167872f /routing.h
parent152a5c8c2cf2fe59f3b61047db41ad3c9c6c9bd2 (diff)
downloadcrow-7ec586556e348725bff3919f4787a75d71c520fa.tar.gz
crow-7ec586556e348725bff3919f4787a75d71c520fa.zip
compiler error on invalid handler type; still no routing for dynamic url
Diffstat (limited to 'routing.h')
-rw-r--r--routing.h205
1 files changed, 178 insertions, 27 deletions
diff --git a/routing.h b/routing.h
index 79e35fd..8c3cc99 100644
--- a/routing.h
+++ b/routing.h
@@ -5,6 +5,8 @@
#include <string>
#include <tuple>
#include <unordered_map>
+#include <memory>
+#include <boost/lexical_cast.hpp>
#include "http_response.h"
@@ -13,17 +15,56 @@
namespace flask
{
- class Rule
+ class BaseRule
{
public:
- explicit Rule(std::string rule)
+ BaseRule(std::string rule)
: rule_(std::move(rule))
{
}
+
+ virtual ~BaseRule()
+ {
+ }
+
+ BaseRule& name(std::string name)
+ {
+ name_ = std::move(name);
+ return *this;
+ }
+
+ virtual void validate()
+ {
+ }
+ virtual response handle(const request&)
+ {
+ return response(400);
+ }
+
+ protected:
+ std::string rule_;
+ std::string name_;
+ };
+
+ class Rule : public BaseRule
+ {
+ public:
+ Rule(std::string rule)
+ : BaseRule(std::move(rule))
+ {
+ }
+ Rule& name(std::string name)
+ {
+ name_ = std::move(name);
+ return *this;
+ }
+
template <typename Func>
void operator()(Func&& f)
{
+ static_assert(black_magic::CallHelper<Func, black_magic::S<>>::value,
+ "Handler type is mismatched with URL paramters");
handler_ = [f = std::move(f)]{
return response(f());
};
@@ -32,41 +73,75 @@ namespace flask
template <typename Func>
void operator()(std::string name, Func&& f)
{
+ static_assert(black_magic::CallHelper<Func, black_magic::S<>>::value,
+ "Handler type is mismatched with URL paramters");
name_ = std::move(name);
handler_ = [f = std::move(f)]{
return response(f());
};
}
- bool match(const request& req)
+ void validate()
{
- // FIXME need url parsing
- return req.url == rule_;
+ if (!handler_)
+ {
+ throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
+ }
}
- Rule& name(std::string name)
+ response handle(const request&)
+ {
+ return handler_();
+ }
+
+ protected:
+ std::function<response()> handler_;
+ };
+
+ template <typename ... Args>
+ class TaggedRule : public BaseRule
+ {
+ public:
+ TaggedRule(std::string rule)
+ : BaseRule(std::move(rule))
+ {
+ }
+
+ TaggedRule<Args...>& name(std::string name)
{
name_ = std::move(name);
return *this;
}
- void validate()
+ template <typename Func>
+ void operator()(Func&& f)
{
- if (!handler_)
- {
- throw std::runtime_error(name_ + (!name_.empty() ? ": " : "") + "no handler for url " + rule_);
- }
+ static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
+ "Handler type is mismatched with URL paramters");
+ handler_ = [f = std::move(f)](Args ... args){
+ return response(f(args...));
+ };
+ }
+
+ template <typename Func>
+ void operator()(std::string name, Func&& f)
+ {
+ static_assert(black_magic::CallHelper<Func, black_magic::S<Args...>>::value,
+ "Handler type is mismatched with URL paramters");
+ name_ = std::move(name);
+ handler_ = [f = std::move(f)](Args ... args){
+ return response(f(args...));
+ };
}
response handle(const request&)
{
- return handler_();
+ //return handler_();
+ return response(500);
}
private:
- std::string rule_;
- std::string name_;
- std::function<response()> handler_;
+ std::function<response(Args...)> handler_;
};
constexpr const size_t INVALID_RULE_INDEX{static_cast<size_t>(-1)};
@@ -77,14 +152,41 @@ namespace flask
{
std::unordered_map<std::string, size_t> children;
size_t rule_index{INVALID_RULE_INDEX};
+ size_t child_for_int{INVALID_RULE_INDEX};
+ size_t child_for_uint{INVALID_RULE_INDEX};
+ size_t child_for_float{INVALID_RULE_INDEX};
+ size_t child_for_str{INVALID_RULE_INDEX};
+ size_t child_for_path{INVALID_RULE_INDEX};
+ bool IsSimpleNode() const
+ {
+ return
+ rule_index == INVALID_RULE_INDEX &&
+ child_for_int == INVALID_RULE_INDEX &&
+ child_for_uint == INVALID_RULE_INDEX &&
+ child_for_float == INVALID_RULE_INDEX &&
+ child_for_str == INVALID_RULE_INDEX &&
+ child_for_path == INVALID_RULE_INDEX;
+ }
};
Trie() : nodes_(1)
{
}
+private:
+ void optimizeNode(Node* node)
+ {
+ for(auto& kv : node->children)
+ {
+ Node* child = &nodes_[kv.second];
+ optimizeNode(child);
+ }
+ }
+
+public:
void optimize()
{
+ optimizeNode(head());
}
size_t find(const request& req, const Node* node = nullptr, size_t pos = 0) const
@@ -116,16 +218,54 @@ namespace flask
void add(const std::string& url, size_t rule_index)
{
- size_t idx = 0;
- for(char c:url)
+ size_t idx{0};
+
+ for(size_t i = 0; i < url.size(); i ++)
{
- std::string piece(&c, 1);
- //std::string piece("/");
- if (!nodes_[idx].children.count(piece))
+ char c = url[i];
+ if (c == '<')
{
- nodes_[idx].children.emplace(piece, new_node());
+ if (url.compare(i, 5, "<int>") == 0)
+ {
+ idx = nodes_[idx].child_for_int = new_node();
+ i += 5;
+ }
+ else if (url.compare(i, 6, "<uint>") == 0)
+ {
+ idx = nodes_[idx].child_for_uint = new_node();
+ i += 6;
+ }
+ else if (url.compare(i, 7, "<float>") == 0 ||
+ url.compare(i, 8, "<double>") == 0)
+ {
+ idx = nodes_[idx].child_for_float = new_node();
+ i += 7;
+ }
+ else if (url.compare(i, 5, "<str>") == 0)
+ {
+ idx = nodes_[idx].child_for_str = new_node();
+ i += 5;
+ }
+ else if (url.compare(i, 6, "<path>") == 0)
+ {
+ idx = nodes_[idx].child_for_path = new_node();
+ i += 6;
+ }
+ else
+ {
+ throw std::runtime_error("Invalid url: " + url + " (" + boost::lexical_cast<std::string>(i) + ")");
+ }
+ i --;
+ }
+ else
+ {
+ std::string piece(&c, 1);
+ if (!nodes_[idx].children.count(piece))
+ {
+ nodes_[idx].children.emplace(piece, new_node());
+ }
+ idx = nodes_[idx].children[piece];
}
- idx = nodes_[idx].children[piece];
}
if (nodes_[idx].rule_index != INVALID_RULE_INDEX)
throw std::runtime_error("handler already exists for " + url);
@@ -154,11 +294,22 @@ namespace flask
class Router
{
public:
+ template <uint64_t N>
+ typename black_magic::arguments<N>::type::template rebind<TaggedRule>& new_rule_tagged(const std::string& rule)
+ {
+ using RuleT = typename black_magic::arguments<N>::type::template rebind<TaggedRule>;
+ auto ruleObject = new RuleT(rule);
+ rules_.emplace_back(ruleObject);
+ trie_.add(rule, rules_.size() - 1);
+ return *ruleObject;
+ }
+
Rule& new_rule(const std::string& rule)
{
- rules_.emplace_back(rule);
+ Rule* r(new Rule(rule));
+ rules_.emplace_back(r);
trie_.add(rule, rules_.size() - 1);
- return rules_.back();
+ return *r;
}
void validate()
@@ -166,7 +317,7 @@ namespace flask
trie_.optimize();
for(auto& rule:rules_)
{
- rule.validate();
+ rule->validate();
}
}
@@ -180,10 +331,10 @@ namespace flask
if (rule_index >= rules_.size())
throw std::runtime_error("Trie internal structure corrupted!");
- return rules_[rule_index].handle(req);
+ return rules_[rule_index]->handle(req);
}
private:
- std::vector<Rule> rules_;
+ std::vector<std::unique_ptr<BaseRule>> rules_;
Trie trie_;
};
}