diff options
author | ipknHama <ipknhama@gmail.com> | 2014-04-13 11:24:06 +0900 |
---|---|---|
committer | ipknHama <ipknhama@gmail.com> | 2014-04-13 11:24:06 +0900 |
commit | 7ec586556e348725bff3919f4787a75d71c520fa (patch) | |
tree | b9ca0d11bbe6637ab4b451e7d14702a4c167872f /routing.h | |
parent | 152a5c8c2cf2fe59f3b61047db41ad3c9c6c9bd2 (diff) | |
download | crow-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.h | 205 |
1 files changed, 178 insertions, 27 deletions
@@ -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_; }; } |