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())> {};
65concept HasFromJsonString =
requires {
67 FromJsonString(std::string_view{}, formats::
parse::To<T>{})
72T ParseFromJsonString(std::string_view json) {
73 if constexpr (HasFromJsonString<T>) {
74 return FromJsonString(json, formats::
parse::To<T>{});
89template <
class Dependencies>
94 static constexpr std::string_view
kName =
"easy-dependencies";
96 DependenciesComponent(
const components::ComponentConfig& config,
const components::ComponentContext& context)
97 : DependenciesBase(config, context),
98 dependencies_(context)
101 Dependencies GetDependencies()
const {
return dependencies_; }
104 Dependencies dependencies_;
109class HttpBase
final {
112 std::function<std::string(
const server::
http::HttpRequest&,
const impl::DependenciesBase&)> function;
123 template <
class Component>
129 component_list_.Append<Component>(name);
130 AddComponentConfig(name, config);
134 template <
class Component>
135 bool TryAddComponent(std::string_view name) {
140 component_list_.Append<Component>(name);
145 void DbSchema(std::string_view schema);
151 void Port(std::uint16_t port);
158 friend class HttpWith;
160 void AddComponentConfig(std::string_view name, std::string_view config);
162 HttpBase(
int argc,
const char*
const argv[]);
168 const char*
const* argv_;
169 std::string static_config_;
172 std::uint16_t port_ = 8080;
179template <
class... Dependency>
180class Dependencies
final :
public Dependency... {
182 explicit Dependencies(
const components::ComponentContext& context) : Dependency{context}... {}
184 static void RegisterOn(HttpBase& app) { (Dependency::RegisterOn(app), ...); }
191template <
class Dependency = Dependencies<>>
192class HttpWith
final {
207 class Callback
final {
209 template <
class Function>
210 Callback(Function func);
212 HttpBase::
Callback Extract() &&
noexcept {
return std::move(callback_); }
215 static Dependency GetDependencies(
const impl::DependenciesBase& deps) {
216 return static_cast<
const DependenciesComponent&>(deps).GetDependencies();
221 HttpWith(
int argc,
const char*
const argv[])
224 impl_.TryAddComponent<DependenciesComponent>(DependenciesComponent::kName);
226 ~HttpWith() { Dependency::RegisterOn(impl_); }
235 std::string_view path,
246 impl_
.Route(path
, std::move(func).Extract()
, methods
);
251 HttpWith&
Get(std::string_view path, Callback&& func) {
257 HttpWith&
Post(std::string_view path, Callback&& func) {
263 HttpWith&
Del(std::string_view path, Callback&& func) {
269 HttpWith&
Put(std::string_view path, Callback&& func) {
275 HttpWith&
Patch(std::string_view path, Callback&& func) {
287 HttpWith&
Port(std::uint16_t port) {
304template <
class Function>
305HttpWith<Dependency>::Callback::Callback(Function func) {
306 using server::
http::HttpRequest;
308 constexpr unsigned kMatches =
309 (std::is_invocable_r_v<formats::json::
Value, Function, formats::json::
Value,
const Dependency&> << 0) |
310 (std::is_invocable_r_v<formats::json::
Value, Function, formats::json::
Value> << 1) |
311 (std::is_invocable_r_v<formats::json::
Value, Function,
const HttpRequest&,
const Dependency&> << 2) |
312 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&,
const Dependency&> << 3) |
313 (std::is_invocable_r_v<formats::json::
Value, Function,
const HttpRequest&> << 4) |
314 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&> << 5);
315 constexpr bool has_single_match = (kMatches == 0 || ((kMatches & (kMatches - 1)) == 0));
318 "Found more than one matching signature, probably due to `auto` usage in parameters. See "
319 "the easy::HttpWith::Callback docs for info on supported signatures"
322 if constexpr (kMatches & 1) {
323 callback_.content_type = http::content_type::kApplicationJson;
324 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
327 }
else if constexpr (kMatches & 2) {
328 callback_.content_type = http::content_type::kApplicationJson;
329 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
332 }
else if constexpr (kMatches & 4) {
333 callback_.content_type = http::content_type::kApplicationJson;
334 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
337 }
else if constexpr (kMatches & 8) {
338 callback_.content_type = http::content_type::kApplicationJson;
339 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
340 return f(req, GetDependencies(deps));
342 }
else if constexpr (kMatches & 16) {
343 callback_.content_type = http::content_type::kApplicationJson;
344 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
347 }
else if constexpr (kMatches & 32) {
348 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
352 using FirstArgument = std::decay_t<
typename impl::FirstFunctionArgument<Function>::type>;
354 std::is_class_v<FirstArgument>,
355 "First function argument should be a class or structure that is JSON pareseable"
358 callback_.content_type = http::content_type::kApplicationJson;
359 callback_.function = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
360 auto arg = impl::ParseFromJsonString<FirstArgument>(req
.RequestBody());
362 if constexpr (std::is_invocable_v<Function, FirstArgument,
const Dependency&>) {
363 return formats
::json
::ToString(formats::json::ValueBuilder{f(std::move(arg), GetDependencies(deps))}
367 std::is_invocable_v<Function, FirstArgument>,
368 "Found no matching signature, probably due to second argument of the provided function. See "
369 "the easy::HttpWith::Callback docs for info on supported signatures"
371 return formats
::json
::ToString(formats::json::ValueBuilder{f(std::move(arg))}.ExtractValue()
);
380 explicit PgDep(
const components::ComponentContext& context);
381 storages::
postgres::
Cluster& pg()
const noexcept {
return *pg_cluster_; }
382 static void RegisterOn(HttpBase& app);
385 storages::
postgres::ClusterPtr pg_cluster_;
391 explicit HttpDep(
const components::ComponentContext& context);
392 clients::http::
Client& http() {
return http_; }
393 static void RegisterOn(HttpBase& app);
396 clients::http::
Client& http_;