33 static constexpr std::string_view kName =
"easy-dependencies";
35 ~DependenciesBase()
override;
39struct FirstFunctionArgument;
41template <
class Return,
class First,
class... Args>
42struct FirstFunctionArgument<Return(First, Args...)
noexcept> {
46template <
class Return,
class First,
class... Args>
47struct FirstFunctionArgument<Return(First, Args...)> {
51template <
class Return,
class Class,
class First,
class... Args>
52struct FirstFunctionArgument<Return (Class::*)(First, Args...)> {
56template <
class Return,
class Class,
class First,
class... Args>
57struct FirstFunctionArgument<Return (Class::*)(First, Args...)
const> {
62struct FirstFunctionArgument : FirstFunctionArgument<
decltype(&std::decay_t<T>::operator())> {};
65using FromJsonStringDetector =
decltype(FromJsonString(std::string_view{}, formats::
parse::To<T>{}));
68T ParseFromJsonString(std::string_view json) {
69 if constexpr (std::is_same_v<meta::DetectedType<FromJsonStringDetector, T>, T>) {
70 return FromJsonString(json, formats::
parse::To<T>{});
85template <
class Dependencies>
90 static constexpr std::string_view
kName =
"easy-dependencies";
92 DependenciesComponent(
const components::ComponentConfig& config,
const components::ComponentContext& context)
93 : DependenciesBase(config, context),
94 dependencies_(context)
97 Dependencies GetDependencies()
const {
return dependencies_; }
100 Dependencies dependencies_;
105class HttpBase
final {
108 std::function<std::string(
const server::
http::HttpRequest&,
const impl::DependenciesBase&)> function;
119 template <
class Component>
125 component_list_.Append<Component>(name);
126 AddComponentConfig(name, config);
130 template <
class Component>
131 bool TryAddComponent(std::string_view name) {
136 component_list_.Append<Component>(name);
141 void DbSchema(std::string_view schema);
147 void Port(std::uint16_t port);
154 friend class HttpWith;
156 void AddComponentConfig(std::string_view name, std::string_view config);
158 HttpBase(
int argc,
const char*
const argv[]);
164 const char*
const* argv_;
165 std::string static_config_;
168 std::uint16_t port_ = 8080;
175template <
class... Dependency>
176class Dependencies
final :
public Dependency... {
178 explicit Dependencies(
const components::ComponentContext& context) : Dependency{context}... {}
180 static void RegisterOn(HttpBase& app) { (Dependency::RegisterOn(app), ...); }
187template <
class Dependency = Dependencies<>>
188class HttpWith
final {
203 class Callback
final {
205 template <
class Function>
206 Callback(Function func);
208 HttpBase::
Callback Extract() &&
noexcept {
return std::move(callback_); }
211 static Dependency GetDependencies(
const impl::DependenciesBase& deps) {
212 return static_cast<
const DependenciesComponent&>(deps).GetDependencies();
217 HttpWith(
int argc,
const char*
const argv[])
220 impl_.TryAddComponent<DependenciesComponent>(DependenciesComponent::kName);
222 ~HttpWith() { Dependency::RegisterOn(impl_); }
231 std::string_view path,
242 impl_
.Route(path
, std::move(func).Extract()
, methods
);
247 HttpWith&
Get(std::string_view path, Callback&& func) {
253 HttpWith&
Post(std::string_view path, Callback&& func) {
259 HttpWith&
Del(std::string_view path, Callback&& func) {
265 HttpWith&
Put(std::string_view path, Callback&& func) {
271 HttpWith&
Patch(std::string_view path, Callback&& func) {
283 HttpWith&
Port(std::uint16_t port) {
300template <
class Function>
301HttpWith<Dependency>::Callback::Callback(Function func) {
302 using server::
http::HttpRequest;
304 constexpr unsigned kMatches =
305 (std::is_invocable_r_v<formats::json::
Value, Function, formats::json::
Value,
const Dependency&> << 0) |
306 (std::is_invocable_r_v<formats::json::
Value, Function, formats::json::
Value> << 1) |
307 (std::is_invocable_r_v<formats::json::
Value, Function,
const HttpRequest&,
const Dependency&> << 2) |
308 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&,
const Dependency&> << 3) |
309 (std::is_invocable_r_v<formats::json::
Value, Function,
const HttpRequest&> << 4) |
310 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&> << 5);
311 constexpr bool has_single_match = (kMatches == 0 || ((kMatches & (kMatches - 1)) == 0));
314 "Found more than one matching signature, probably due to `auto` usage in parameters. See "
315 "the easy::HttpWith::Callback docs for info on supported signatures"
318 if constexpr (kMatches & 1) {
319 callback_.content_type =
http::content_type::kApplicationJson;
320 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
323 }
else if constexpr (kMatches & 2) {
324 callback_.content_type =
http::content_type::kApplicationJson;
325 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
328 }
else if constexpr (kMatches & 4) {
329 callback_.content_type =
http::content_type::kApplicationJson;
330 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
333 }
else if constexpr (kMatches & 8) {
334 callback_.content_type =
http::content_type::kApplicationJson;
335 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
336 return f(req, GetDependencies(deps));
338 }
else if constexpr (kMatches & 16) {
339 callback_.content_type =
http::content_type::kApplicationJson;
340 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
343 }
else if constexpr (kMatches & 32) {
344 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
348 using FirstArgument = std::decay_t<
typename impl::FirstFunctionArgument<Function>::type>;
350 std::is_class_v<FirstArgument>,
351 "First function argument should be a class or structure that is JSON pareseable"
354 callback_.content_type =
http::content_type::kApplicationJson;
355 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
356 auto arg = impl::ParseFromJsonString<FirstArgument>(req
.RequestBody());
358 if constexpr (std::is_invocable_v<Function, FirstArgument,
const Dependency&>) {
359 return formats
::json
::ToString(formats::json::ValueBuilder{f(std::move(arg), GetDependencies(deps))}
363 std::is_invocable_v<Function, FirstArgument>,
364 "Found no matching signature, probably due to second argument of the provided function. See "
365 "the easy::HttpWith::Callback docs for info on supported signatures"
367 return formats
::json
::ToString(formats::json::ValueBuilder{f(std::move(arg))}.ExtractValue()
);
376 explicit PgDep(
const components::ComponentContext& context);
377 storages::
postgres::
Cluster& pg()
const noexcept {
return *pg_cluster_; }
378 static void RegisterOn(HttpBase& app);
381 storages::
postgres::ClusterPtr pg_cluster_;
387 explicit HttpDep(
const components::ComponentContext& context);
388 clients::http::
Client& http() {
return http_; }
389 static void RegisterOn(HttpBase& app);
392 clients::http::
Client& http_;