32 static constexpr std::string_view kName =
"easy-dependencies";
34 ~DependenciesBase()
override;
38struct FirstFunctionArgument;
40template <
class Return,
class First,
class... Args>
41struct FirstFunctionArgument<Return(First, Args...)
noexcept> {
45template <
class Return,
class First,
class... Args>
46struct FirstFunctionArgument<Return(First, Args...)> {
50template <
class Return,
class Class,
class First,
class... Args>
51struct FirstFunctionArgument<Return (Class::*)(First, Args...)> {
55template <
class Return,
class Class,
class First,
class... Args>
56struct FirstFunctionArgument<Return (Class::*)(First, Args...)
const> {
61struct FirstFunctionArgument : FirstFunctionArgument<
decltype(&std::decay_t<T>::operator())> {};
64using FromJsonStringDetector =
decltype(FromJsonString(std::string_view{}, formats::
parse::To<T>{}));
67T ParseFromJsonString(std::string_view json) {
68 if constexpr (std::is_same_v<meta::DetectedType<FromJsonStringDetector, T>, T>) {
69 return FromJsonString(json, formats::
parse::To<T>{});
84template <
class Dependencies>
89 static constexpr std::string_view
kName =
"easy-dependencies";
91 DependenciesComponent(
const components::ComponentConfig& config,
const components::ComponentContext& context)
92 : DependenciesBase(config, context),
93 dependencies_(context)
96 Dependencies GetDependencies()
const {
return dependencies_; }
99 Dependencies dependencies_;
104class HttpBase
final {
106 using Callback = std::function<std::string(
const server::
http::HttpRequest&,
const impl::DependenciesBase&)>;
112 void Route(std::string_view path, Callback&& func, std::initializer_list<server::
http::
HttpMethod> methods);
115 template <
class Component>
121 component_list_.Append<Component>(name);
122 AddComponentConfig(name, config);
126 template <
class Component>
127 bool TryAddComponent(std::string_view name) {
132 component_list_.Append<Component>(name);
137 void DbSchema(std::string_view schema);
143 void Port(std::uint16_t port);
150 friend class HttpWith;
152 void AddComponentConfig(std::string_view name, std::string_view config);
154 HttpBase(
int argc,
const char*
const argv[]);
160 const char*
const* argv_;
161 std::string static_config_;
164 std::uint16_t port_ = 8080;
171template <
class... Dependency>
172class Dependencies
final :
public Dependency... {
174 explicit Dependencies(
const components::ComponentContext& context) : Dependency{context}... {}
176 static void RegisterOn(HttpBase& app) { (Dependency::RegisterOn(app), ...); }
183template <
class Dependency = Dependencies<>>
184class HttpWith
final {
199 class Callback
final {
201 template <
class Function>
202 Callback(Function func);
204 HttpBase::Callback Extract() &&
noexcept {
return std::move(func_); }
207 static Dependency GetDependencies(
const impl::DependenciesBase& deps) {
208 return static_cast<
const DependenciesComponent&>(deps).GetDependencies();
210 HttpBase::Callback func_;
213 HttpWith(
int argc,
const char*
const argv[])
216 impl_.TryAddComponent<DependenciesComponent>(DependenciesComponent::kName);
218 ~HttpWith() { Dependency::RegisterOn(impl_); }
227 std::string_view path,
238 impl_
.Route(path
, std::move(func).Extract()
, methods
);
243 HttpWith&
Get(std::string_view path, Callback&& func) {
249 HttpWith&
Post(std::string_view path, Callback&& func) {
255 HttpWith&
Del(std::string_view path, Callback&& func) {
261 HttpWith&
Put(std::string_view path, Callback&& func) {
267 HttpWith&
Patch(std::string_view path, Callback&& func) {
279 HttpWith&
Port(std::uint16_t port) {
296template <
class Function>
297HttpWith<Dependency>::Callback::Callback(Function func) {
298 using server::
http::HttpRequest;
300 constexpr unsigned kMatches =
301 (std::is_invocable_r_v<formats::json::
Value, Function, formats::json::
Value,
const Dependency&> << 0) |
302 (std::is_invocable_r_v<formats::json::
Value, Function, formats::json::
Value> << 1) |
303 (std::is_invocable_r_v<formats::json::
Value, Function,
const HttpRequest&,
const Dependency&> << 2) |
304 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&,
const Dependency&> << 3) |
305 (std::is_invocable_r_v<formats::json::
Value, Function,
const HttpRequest&> << 4) |
306 (std::is_invocable_r_v<std::string, Function,
const HttpRequest&> << 5);
307 constexpr bool has_single_match = (kMatches == 0 || ((kMatches & (kMatches - 1)) == 0));
310 "Found more than one matching signature, probably due to `auto` usage in parameters. See "
311 "the easy::HttpWith::Callback docs for info on supported signatures"
314 if constexpr (kMatches & 1) {
315 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
319 }
else if constexpr (kMatches & 2) {
320 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
324 }
else if constexpr (kMatches & 4) {
325 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
329 }
else if constexpr (kMatches & 8) {
330 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
331 return f(req, GetDependencies(deps));
333 }
else if constexpr (kMatches & 16) {
334 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
338 }
else if constexpr (kMatches & 32) {
339 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase&) {
return f(req); };
341 using FirstArgument = std::decay_t<
typename impl::FirstFunctionArgument<Function>::type>;
343 std::is_class_v<FirstArgument>,
344 "First function argument should be a class or structure that is JSON pareseable"
347 func_ = [f = std::move(func)](
const HttpRequest& req,
const impl::DependenciesBase& deps) {
349 auto arg = impl::ParseFromJsonString<FirstArgument>(req
.RequestBody());
351 if constexpr (std::is_invocable_v<Function, FirstArgument,
const Dependency&>) {
352 return formats
::json
::ToString(formats::json::ValueBuilder{f(std::move(arg), GetDependencies(deps))}
356 std::is_invocable_v<Function, FirstArgument>,
357 "Found no matching signature, probably due to second argument of the provided function. See "
358 "the easy::HttpWith::Callback docs for info on supported signatures"
360 return formats
::json
::ToString(formats::json::ValueBuilder{f(std::move(arg))}.ExtractValue()
);
369 explicit PgDep(
const components::ComponentContext& context);
370 storages::
postgres::
Cluster& pg()
const noexcept {
return *pg_cluster_; }
371 static void RegisterOn(HttpBase& app);
374 storages::
postgres::ClusterPtr pg_cluster_;
380 explicit HttpDep(
const components::ComponentContext& context);
381 clients::http::
Client& http() {
return http_; }
382 static void RegisterOn(HttpBase& app);
385 clients::http::
Client& http_;