#include #include #include #include #include #include #include #include #include #include #include struct Image { std::vector bitmap_; }; struct Credentail { int cert_; }; struct DownloadRequest { enum class ConnectionType { Telnet, Ssh }; std::string url_; Credentail credentail_; int maxMbps_; bool cache_; ConnectionType type_; }; struct UploadRequest { enum class ConnectionType { Telnet, Ssh }; std::string url_; Credentail credentail_; int maxMbps_; int size_; ConnectionType type_; }; class Command { public: enum class ErrorCode { Ok, WrongUrl, CanNotConnect, WrongCredential, MaximumSizeExceeded, MissingImage, }; class Delegate { public: virtual ~Delegate() = default; virtual ErrorCode removeFromCache(const std::string& url) = 0; virtual void clearCache() = 0; virtual void addToCache(const std::vector& data, const std::string& url) = 0; virtual ErrorCode download(std::vector& data, const DownloadRequest& request) const = 0; virtual ErrorCode upload(const std::vector& data, const UploadRequest& request) const = 0; }; explicit Command(Delegate* delegate) : delegate_(delegate) {} // Rule of 5! virtual ~Command() = default; Command(const Command&) = default; Command(Command&&) = default; Command& operator=(const Command&) = default; Command& operator=(Command&&) = default; virtual void operator()() const = 0; protected: Delegate* delegate_; }; class DownloadImageCommand : public Command { public: using CallbackType = void (*)(ErrorCode, Image); ~DownloadImageCommand() override = default; DownloadImageCommand(Delegate* delegate, CallbackType callback, const DownloadRequest& request) : Command(delegate), callback_(callback), request_(request) {} void operator()() const override { std::vector data; if (auto ec = delegate_->download(data, request_); ec == Command::ErrorCode::Ok) { // Do some conversion on vector if (request_.cache_) { delegate_->addToCache(data, request_.url_); } Image image{data}; callback_(ec, std::move(image)); } else { callback_(ec, Image{}); } }; private: CallbackType callback_; DownloadRequest request_; }; class UploadImageCommand : public Command { public: using CallbackType = void (*)(ErrorCode); ~UploadImageCommand() override = default; UploadImageCommand(Delegate* delegate, const Image& image, CallbackType callback, const UploadRequest& request) : Command(delegate), image_(image), callback_(callback), request_(request) {} void operator()() const override { callback_(delegate_->upload(image_.bitmap_, request_)); }; private: Image image_; CallbackType callback_; UploadRequest request_; }; class Server : public Command::Delegate { public: ~Server() override = default; Command::ErrorCode removeFromCache(const std::string& url) override { if (const auto it = findFile(url); it != std::cend(cache_)) { cache_.erase(it); return Command::ErrorCode::Ok; } return Command::ErrorCode::MissingImage; } void clearCache() override { cache_.clear(); } void addToCache(const std::vector& data, const std::string& url) override { cache_.emplace_back(data, url); } Command::ErrorCode download(std::vector& data, const DownloadRequest& request) const override { if (request.type_ == DownloadRequest::ConnectionType::Ssh && request.credentail_.cert_ != 123) { return Command::ErrorCode::WrongCredential; } if (request.type_ == DownloadRequest::ConnectionType::Telnet && request.credentail_.cert_ != 231) { return Command::ErrorCode::CanNotConnect; } // Simulate some other error if (request.maxMbps_ % 2) { return Command::ErrorCode::WrongUrl; } data = {97, 98, 99, 100, 101, 102}; return Command::ErrorCode::Ok; } Command::ErrorCode upload(const std::vector& data, const UploadRequest& request) const override { if (request.type_ == UploadRequest::ConnectionType::Ssh && request.credentail_.cert_ != 1234) { return Command::ErrorCode::WrongCredential; } if (request.type_ == UploadRequest::ConnectionType::Telnet && request.credentail_.cert_ != 5432) { return Command::ErrorCode::CanNotConnect; } // Simulate some other error if (!request.maxMbps_ % 2) { return Command::ErrorCode::WrongUrl; } return Command::ErrorCode::Ok; } private: std::vector, std::string>>::const_iterator findFile(const std::string& url) const { return std::find_if(cbegin(cache_), cend(cache_), [url](const auto& pair) { const auto& [data, this_url] = pair; return this_url == url; }); } std::vector, std::string>> cache_; }; class RequestHandler { public: void start() { std::thread(&RequestHandler::run, this).detach(); } void stop() { stop_ = true; cv_.notify_one(); } void pushRequest(std::unique_ptr command) { { std::lock_guard lock(m_); requests_.push(std::move(command)); } cv_.notify_one(); } private: void run() { while (!stop_) { const auto request = waitForRequest(); if (!request || !*request) { return; } (**request)(); } } std::optional> waitForRequest() { std::unique_lock lk(m_); cv_.wait(lk, [&]() { return !requests_.empty() || stop_; }); if (stop_) { return std::nullopt; } std::unique_ptr request = std::move(requests_.front()); requests_.pop(); return std::move(request); } std::mutex m_; std::condition_variable cv_; std::atomic stop_{false}; std::queue> requests_; }; int main() { Server server; RequestHandler handler; handler.start(); handler.pushRequest(std::make_unique( &server, [](Command::ErrorCode ec, Image img) { if (ec == Command::ErrorCode::Ok) { std::copy(cbegin(img.bitmap_), cend(img.bitmap_), std::ostream_iterator(std::cout, " ")); std::cout << '\n'; } else { std::cout << "Sth went wrong!\n"; } }, DownloadRequest{"Sth123", Credentail{123}, 200, true, DownloadRequest::ConnectionType::Ssh})); std::this_thread::sleep_for(std::chrono::milliseconds(100)); handler.stop(); }