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

234 lines
No EOL
6.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 Credentail {
int cert_;
};
struct Download {
enum class ConnectionType { Telnet,
Ssh };
std::string url_;
Credentail credentail_;
int maxMbps_;
bool cache_;
ConnectionType type_;
};
struct Upload {
enum class ConnectionType { Telnet,
Ssh };
std::string url_;
Credentail credentail_;
int maxMbps_;
int size_;
ConnectionType type_;
};
struct RemoveFromCache {
std::string url_;
};
struct ClearCache {
};
struct Image {
std::vector<uint8_t> bitmap_;
};
class Server {
public:
enum class ErrorCode {
Ok,
WrongUrl,
CanNotConnect,
WrongCredential,
MaximumSizeExceeded,
MissingImage,
};
using CallbackType = void (*)(ErrorCode, Image);
void handle(const Download& request, CallbackType callback) {
if (const auto it = findImage(request.url_); it != std::cend(cache_)) {
callback(ErrorCode::Ok, it->first);
return;
}
Image img;
const auto ec = download(request, img);
if (ec == ErrorCode::Ok) {
if (request.cache_) {
cache_.emplace_back(img, request.url_);
}
}
callback(ec, img);
}
void handle(const Upload& request, CallbackType callback) const {
if (request.size_ > 100) {
callback(ErrorCode::MaximumSizeExceeded, Image{});
return;
}
Image img;
callback(upload(request, img), img);
}
void handle(const RemoveFromCache& request, CallbackType callback) {
if (const auto it = findImage(request.url_); it != std::cend(cache_)) {
cache_.erase(it);
callback(ErrorCode::Ok, Image{});
return;
}
callback(ErrorCode::MissingImage, Image{});
}
void handle(const ClearCache& request, CallbackType callback) {
cache_.clear();
callback(ErrorCode::Ok, Image{});
}
private:
std::vector<std::pair<Image, std::string>>::const_iterator findImage(const std::string& url) const {
return std::find_if(cbegin(cache_), cend(cache_),
[url](const auto& pair) {
const auto& [image, this_url] = pair;
return this_url == url;
});
}
ErrorCode download(const Download& request, Image& image) const {
if (request.type_ == Download::ConnectionType::Ssh && request.credentail_.cert_ != 123) {
return ErrorCode::WrongCredential;
}
if (request.type_ == Download::ConnectionType::Telnet && request.credentail_.cert_ != 231) {
return ErrorCode::CanNotConnect;
}
// Simulate some other error
if (request.maxMbps_ % 2) {
return ErrorCode::WrongUrl;
}
image.bitmap_ = {97, 98, 99, 100, 101, 102};
return ErrorCode::Ok;
}
ErrorCode upload(const Upload& request, const Image& image) const {
if (request.type_ == Upload::ConnectionType::Ssh && request.credentail_.cert_ != 1234) {
return ErrorCode::WrongCredential;
}
if (request.type_ == Upload::ConnectionType::Telnet && request.credentail_.cert_ != 5432) {
return ErrorCode::CanNotConnect;
}
// Simulate some other error
if (!request.maxMbps_ % 2) {
return ErrorCode::WrongUrl;
}
return ErrorCode::Ok;
}
std::vector<std::pair<Image, std::string>> cache_;
};
class RequestHandler {
public:
using RequestType = std::variant<Download, Upload, RemoveFromCache, ClearCache>;
void start(Server* server) {
std::thread(&RequestHandler::run, this, server).detach();
}
void stop() {
stop_ = true;
cv_.notify_one();
}
void pushRequest(const RequestType& request, Server::CallbackType callback) {
{
std::lock_guard lock(m_);
requests_.emplace(request, callback);
}
cv_.notify_one();
}
private:
using QueueType = std::pair<RequestType, Server::CallbackType>;
void run(Server* server) {
while (!stop_) {
const auto res = waitForRequest();
if (!res) {
return;
}
const auto [request, callback] = *res;
switch (request.index()) {
case 0:
server->handle(std::get<Download>(request), callback);
break;
case 1:
server->handle(std::get<Upload>(request), callback);
break;
case 2:
server->handle(std::get<RemoveFromCache>(request), callback);
break;
case 3:
server->handle(std::get<ClearCache>(request), callback);
break;
}
}
}
std::optional<QueueType> waitForRequest() {
std::unique_lock lk(m_);
cv_.wait(lk, [&]() { return !requests_.empty() || stop_; });
if (stop_) {
return std::nullopt;
}
auto pair = requests_.front();
requests_.pop();
return pair;
}
std::mutex m_;
std::condition_variable cv_;
std::atomic<bool> stop_{false};
std::queue<QueueType> requests_;
};
int main() {
Server server;
RequestHandler handler;
handler.start(&server);
handler.pushRequest(Download{"sth.png", 123, 100, true, Download::ConnectionType::Ssh},
[](Server::ErrorCode ec, Image image) {
if (ec == Server::ErrorCode::Ok) {
for (auto el : image.bitmap_) {
std::cout << el << ' ';
}
std::cout << '\n';
} else {
std::cout << "FAILED!\n";
}
});
std::this_thread::sleep_for(std::chrono::milliseconds(10));
handler.stop();
}