diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index cb9be6a29..5330892c7 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -73,6 +73,10 @@ else () target_compile_options(example_json_map PRIVATE "${compiler_options}") target_link_libraries(example_json_map PUBLIC ${REQUIRED_LIBRARIES}) + add_executable(example_blueprint example_blueprint.cpp) + target_compile_options(example_blueprint PRIVATE "${compiler_options}") + target_link_libraries(example_blueprint PUBLIC ${REQUIRED_LIBRARIES}) + add_custom_command(OUTPUT example_chat.html COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/example_chat.html ${CMAKE_CURRENT_BINARY_DIR}/example_chat.html diff --git a/examples/example_blueprint.cpp b/examples/example_blueprint.cpp new file mode 100644 index 000000000..a9dae7220 --- /dev/null +++ b/examples/example_blueprint.cpp @@ -0,0 +1,18 @@ +#define CROW_MAIN +#include "crow.h" + +int main() +{ + crow::SimpleApp app; + + crow::Blueprint bp("bp_prefix"); + + CROW_BP_ROUTE(app, bp, "/") + ([]() { + return "Hello world!"; + }); + + app.register_blueprint(bp); + + app.port(18080).run(); +} diff --git a/include/crow/app.h b/include/crow/app.h index 1c9f03e7a..f4cf5eb50 100644 --- a/include/crow/app.h +++ b/include/crow/app.h @@ -24,8 +24,10 @@ #ifdef CROW_MSVC_WORKAROUND #define CROW_ROUTE(app, url) app.route_dynamic(url) +#define CROW_BP_ROUTE(app, blueprint, url) app.route_dynamic(blueprint, url) #else #define CROW_ROUTE(app, url) app.route(url) +#define CROW_BP_ROUTE(app, blueprint, url) app.route(blueprint, url) #endif #define CROW_CATCHALL_ROUTE(app) app.catchall_route() @@ -80,14 +82,28 @@ namespace crow return router_.new_rule_dynamic(std::move(rule)); } + ///Create a dynamic route for a blueprint using a rule (**Use CROW_ROUTE instead**) + DynamicRule& route_dynamic(Blueprint& blueprint, std::string&& rule) + { + return blueprint.new_rule_dynamic(std::move(rule)); + } + ///Create a route using a rule (**Use CROW_ROUTE instead**) template auto route(std::string&& rule) -> typename std::result_of)(Router, std::string&&)>::type - { + {//TODO process the blueprint return router_.new_rule_tagged(std::move(rule)); } + ///Create a route for a blueprint using a rule (**Use CROW_ROUTE instead**) + template + auto route(Blueprint& blueprint, std::string&& rule) + -> typename std::result_of)(Router, std::string&&)>::type + { + return blueprint.new_rule_tagged(std::move(rule)); + } + ///Create a route for any requests without a proper route (**Use CROW_CATCHALL_ROUTE instead**) CatchallRule& catchall_route() { @@ -158,12 +174,21 @@ namespace crow /// crow::LogLevel::Warning (2)
/// crow::LogLevel::Error (3)
/// crow::LogLevel::Critical (4)
- self_t& loglevel(crow::LogLevel level) + self_t& loglevel(LogLevel level) { crow::logger::setLogLevel(level); return *this; } + self_t& register_blueprint(Blueprint& blueprint) + { + if (blueprints_.empty() || std::find(blueprints_.begin(), blueprints_.end(), &blueprint) == blueprints_.end()) + { + blueprints_.emplace_back(&blueprint); + }//TODO error throwing + return *this; + } + ///Set a custom duration and function to run on every tick template self_t& tick(Duration d, Func f) { @@ -191,7 +216,7 @@ namespace crow ///Go through the rules, upgrade them if possible, and add them to the list of rules void validate() { - router_.validate(); + router_.validate(blueprints_); } ///Notify anything using `wait_for_server_start()` to proceed @@ -380,6 +405,8 @@ namespace crow std::vector signals_{SIGINT, SIGTERM}; + std::vector blueprints_; + bool server_started_{false}; std::condition_variable cv_started_; std::mutex start_mutex_; diff --git a/include/crow/routing.h b/include/crow/routing.h index f31c8386b..25be2bff1 100644 --- a/include/crow/routing.h +++ b/include/crow/routing.h @@ -1029,6 +1029,94 @@ namespace crow std::vector nodes_; }; + /// A blueprint can be considered a smaller section of a Crow app, specifically where the router is conecerned. + class Blueprint + { + public: + Blueprint() + { + } + + Blueprint(const std::string& prefix): + prefix_(prefix){}; + + Blueprint(std::string&& prefix): + prefix_(prefix){}; +/* + Blueprint(Blueprint& other) + { + prefix_ = std::move(other.prefix_); + all_rules_ = std::move(other.all_rules_); + } + + Blueprint(const Blueprint& other) + { + prefix_ = other.prefix_; + all_rules_ = other.all_rules_; + } +*/ + Blueprint(Blueprint&& value) + { + *this = std::move(value); + } + + Blueprint& operator = (const Blueprint& value) = delete; + + Blueprint& operator = (Blueprint&& value) noexcept + { + prefix_ = std::move(value.prefix_); + all_rules_ = std::move(value.all_rules_); + catchall_rule_ = std::move(value.catchall_rule_); + return *this; + } + + bool operator == (const Blueprint& value) + { + return value.prefix() == prefix_; + } + + bool operator != (const Blueprint& value) + { + return value.prefix() != prefix_; + } + + std::string prefix() const + { + return prefix_; + } + + DynamicRule& new_rule_dynamic(const std::string& rule) + { + std::string new_rule = '/' + prefix_ + rule; + auto ruleObject = new DynamicRule(new_rule); + all_rules_.emplace_back(ruleObject); + + return *ruleObject; + } + + template + typename black_magic::arguments::type::template rebind& new_rule_tagged(const std::string& rule) + { + std::string new_rule = '/' + prefix_ + rule; + using RuleT = typename black_magic::arguments::type::template rebind; + + auto ruleObject = new RuleT(new_rule); + all_rules_.emplace_back(ruleObject); + + return *ruleObject; + } + + CatchallRule& catchall_rule() + { + return catchall_rule_; + } + + private: + std::string prefix_; + std::vector> all_rules_; + CatchallRule catchall_rule_; + friend class Router; + }; /// Handles matching requests to existing rules and upgrade requests. class Router @@ -1088,8 +1176,20 @@ namespace crow } - void validate() + void validate(std::vector& bp_list) { + //Take all the routes from the registered blueprints and add them to `all_rules_` to be processed. + if (!(bp_list.empty())) + { + for (Blueprint* bp : bp_list) + { + for (auto& rule: bp->all_rules_) + { + all_rules_.push_back(std::move(rule)); + } + } + } + for(auto& rule:all_rules_) { if (rule)