trainings/CreatingReliableSoftwareCpp/Presentation/examples/refactoring/soultions/refactoring2.cpp

261 lines
No EOL
7.2 KiB
C++

#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <iostream>
#include <memory>
#include <mutex>
#include <optional>
#include <thread>
#include <variant>
#include <vector>
struct Image {
std::vector<uint8_t> 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<uint8_t>& data, const std::string& url) = 0;
virtual ErrorCode download(std::vector<uint8_t>& data, const DownloadRequest& request) const = 0;
virtual ErrorCode upload(const std::vector<uint8_t>& 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<uint8_t> 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<uint8_t>& data, const std::string& url) override {
cache_.emplace_back(data, url);
}
Command::ErrorCode download(std::vector<uint8_t>& 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<uint8_t>& 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::pair<std::vector<uint8_t>, 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::pair<std::vector<uint8_t>, 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> 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<std::unique_ptr<Command>> waitForRequest() {
std::unique_lock lk(m_);
cv_.wait(lk, [&]() { return !requests_.empty() || stop_; });
if (stop_) {
return std::nullopt;
}
std::unique_ptr<Command> request = std::move(requests_.front());
requests_.pop();
return std::move(request);
}
std::mutex m_;
std::condition_variable cv_;
std::atomic<bool> stop_{false};
std::queue<std::unique_ptr<Command>> requests_;
};
int main() {
Server server;
RequestHandler handler;
handler.start();
handler.pushRequest(std::make_unique<DownloadImageCommand>(
&server,
[](Command::ErrorCode ec, Image img) {
if (ec == Command::ErrorCode::Ok) {
std::copy(cbegin(img.bitmap_), cend(img.bitmap_), std::ostream_iterator<uint8_t>(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();
}