mirror of
https://github.com/logos-storage/logos-storage-nim.git
synced 2026-06-27 12:59:30 +00:00
290 lines
8.0 KiB
C++
290 lines
8.0 KiB
C++
#include "storage_client.hpp"
|
|
|
|
#include <cstdlib>
|
|
#include <mutex>
|
|
#include <stdexcept>
|
|
#include <utility>
|
|
|
|
extern "C" {
|
|
extern void libstorageNimMain(void);
|
|
}
|
|
|
|
namespace {
|
|
void ensureNimRuntime() {
|
|
static std::once_flag once;
|
|
std::call_once(once, [] { libstorageNimMain(); });
|
|
}
|
|
|
|
bool isOk(int ret) {
|
|
return ret == RET_OK;
|
|
}
|
|
|
|
std::string takeCString(char* value) {
|
|
if (!value) {
|
|
return {};
|
|
}
|
|
|
|
std::string result(value);
|
|
std::free(value);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
void StorageResponse::setResult(int callerRet, const char* msg, size_t len) {
|
|
std::lock_guard<std::mutex> lock(mtx_);
|
|
|
|
if (callerRet == RET_PROGRESS) {
|
|
progressCount_ += 1;
|
|
if (msg && len > 0) {
|
|
lastProgress_.assign(msg, len);
|
|
} else {
|
|
lastProgress_.clear();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (msg && len > 0) {
|
|
result_.assign(msg, len);
|
|
} else {
|
|
result_.clear();
|
|
}
|
|
|
|
status_ = callerRet;
|
|
done_ = true;
|
|
cv_.notify_one();
|
|
}
|
|
|
|
bool StorageResponse::wait(std::chrono::milliseconds timeout) {
|
|
std::unique_lock<std::mutex> lock(mtx_);
|
|
if (timeout.count() == 0) {
|
|
cv_.wait(lock, [this] { return done_; });
|
|
return true;
|
|
}
|
|
return cv_.wait_for(lock, timeout, [this] { return done_; });
|
|
}
|
|
|
|
int StorageResponse::status() const {
|
|
std::lock_guard<std::mutex> lock(mtx_);
|
|
return status_;
|
|
}
|
|
|
|
std::string StorageResponse::data() const {
|
|
std::lock_guard<std::mutex> lock(mtx_);
|
|
return result_;
|
|
}
|
|
|
|
size_t StorageResponse::progressCount() const {
|
|
std::lock_guard<std::mutex> lock(mtx_);
|
|
return progressCount_;
|
|
}
|
|
|
|
std::string StorageResponse::lastProgress() const {
|
|
std::lock_guard<std::mutex> lock(mtx_);
|
|
return lastProgress_;
|
|
}
|
|
|
|
StorageClient::StorageClient(std::string configJson, std::chrono::milliseconds timeout)
|
|
: timeout_(timeout) {
|
|
ensureNimRuntime();
|
|
|
|
StorageResponse resp;
|
|
ctx_ = storage_new(configJson.c_str(), callback, &resp);
|
|
if (!ctx_) {
|
|
throw std::runtime_error("storage_new returned null context");
|
|
}
|
|
|
|
if (!resp.wait(timeout_)) {
|
|
storage_destroy(ctx_);
|
|
ctx_ = nullptr;
|
|
throw std::runtime_error("storage_new timed out");
|
|
}
|
|
|
|
if (!isOk(resp.status())) {
|
|
std::string error = resp.data();
|
|
storage_destroy(ctx_);
|
|
ctx_ = nullptr;
|
|
throw std::runtime_error("storage_new failed: " + error);
|
|
}
|
|
}
|
|
|
|
StorageClient::~StorageClient() {
|
|
if (!ctx_) {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
if (started_) {
|
|
stop();
|
|
}
|
|
if (!closed_) {
|
|
close();
|
|
}
|
|
} catch (...) {
|
|
// Destructors must not throw; callers should use explicit stop/close for errors.
|
|
}
|
|
|
|
storage_destroy(ctx_);
|
|
}
|
|
|
|
void StorageClient::start() {
|
|
call("storage_start", [this](StorageCallback cb, void* userData) {
|
|
return storage_start(ctx_, cb, userData);
|
|
});
|
|
started_ = true;
|
|
}
|
|
|
|
void StorageClient::stop() {
|
|
call("storage_stop", [this](StorageCallback cb, void* userData) {
|
|
return storage_stop(ctx_, cb, userData);
|
|
});
|
|
started_ = false;
|
|
}
|
|
|
|
void StorageClient::close() {
|
|
call("storage_close", [this](StorageCallback cb, void* userData) {
|
|
return storage_close(ctx_, cb, userData);
|
|
});
|
|
closed_ = true;
|
|
}
|
|
|
|
std::string StorageClient::version() const {
|
|
return takeCString(storage_version(ctx_));
|
|
}
|
|
|
|
std::string StorageClient::revision() const {
|
|
return takeCString(storage_revision(ctx_));
|
|
}
|
|
|
|
std::string StorageClient::repo() {
|
|
return call("storage_repo", [this](StorageCallback cb, void* userData) {
|
|
return storage_repo(ctx_, cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::peerId() {
|
|
return call("storage_peer_id", [this](StorageCallback cb, void* userData) {
|
|
return storage_peer_id(ctx_, cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::metrics() {
|
|
return call("storage_get_metrics", [this](StorageCallback cb, void* userData) {
|
|
return storage_get_metrics(ctx_, cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::debug() {
|
|
return call("storage_debug", [this](StorageCallback cb, void* userData) {
|
|
return storage_debug(ctx_, cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::spr() {
|
|
return call("storage_spr", [this](StorageCallback cb, void* userData) {
|
|
return storage_spr(ctx_, cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::list() {
|
|
return call("storage_list", [this](StorageCallback cb, void* userData) {
|
|
return storage_list(ctx_, cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::space() {
|
|
return call("storage_space", [this](StorageCallback cb, void* userData) {
|
|
return storage_space(ctx_, cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::manifest(const std::string& cid) {
|
|
return call("storage_download_manifest", [this, &cid](StorageCallback cb, void* userData) {
|
|
return storage_download_manifest(ctx_, cid.c_str(), cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::exists(const std::string& cid) {
|
|
return call("storage_exists", [this, &cid](StorageCallback cb, void* userData) {
|
|
return storage_exists(ctx_, cid.c_str(), cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::deleteContent(const std::string& cid) {
|
|
return call("storage_delete", [this, &cid](StorageCallback cb, void* userData) {
|
|
return storage_delete(ctx_, cid.c_str(), cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::fetch(const std::string& cid) {
|
|
return call("storage_fetch", [this, &cid](StorageCallback cb, void* userData) {
|
|
return storage_fetch(ctx_, cid.c_str(), cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::connect(
|
|
const std::string& peerId,
|
|
const std::vector<std::string>& peerAddresses) {
|
|
std::vector<const char*> addresses;
|
|
addresses.reserve(peerAddresses.size());
|
|
for (const auto& address : peerAddresses) {
|
|
addresses.push_back(address.c_str());
|
|
}
|
|
|
|
return call("storage_connect", [this, &peerId, &addresses](StorageCallback cb, void* userData) {
|
|
return storage_connect(ctx_, peerId.c_str(), addresses.data(), addresses.size(), cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::uploadFile(const std::string& filepath, size_t chunkSize) {
|
|
const std::string sessionId = call("storage_upload_init", [this, &filepath, chunkSize](
|
|
StorageCallback cb,
|
|
void* userData) {
|
|
return storage_upload_init(ctx_, filepath.c_str(), chunkSize, cb, userData);
|
|
});
|
|
|
|
return call("storage_upload_file", [this, &sessionId](StorageCallback cb, void* userData) {
|
|
return storage_upload_file(ctx_, sessionId.c_str(), cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::downloadFile(
|
|
const std::string& cid,
|
|
const std::string& outputPath,
|
|
size_t chunkSize,
|
|
bool local) {
|
|
call("storage_download_init", [this, &cid, chunkSize, local](StorageCallback cb, void* userData) {
|
|
return storage_download_init(ctx_, cid.c_str(), chunkSize, local, cb, userData);
|
|
});
|
|
|
|
return call("storage_download_stream", [this, &cid, &outputPath, chunkSize, local](
|
|
StorageCallback cb,
|
|
void* userData) {
|
|
return storage_download_stream(
|
|
ctx_, cid.c_str(), chunkSize, local, outputPath.c_str(), cb, userData);
|
|
});
|
|
}
|
|
|
|
std::string StorageClient::call(const char* name, const AsyncCall& fn) {
|
|
StorageResponse resp;
|
|
const int dispatchRet = fn(callback, &resp);
|
|
if (!isOk(dispatchRet)) {
|
|
throw std::runtime_error(std::string(name) + " dispatch failed");
|
|
}
|
|
|
|
if (!resp.wait(timeout_)) {
|
|
throw std::runtime_error(std::string(name) + " timed out");
|
|
}
|
|
|
|
if (!isOk(resp.status())) {
|
|
throw std::runtime_error(std::string(name) + " failed: " + resp.data());
|
|
}
|
|
|
|
return resp.data();
|
|
}
|
|
|
|
void StorageClient::callback(int callerRet, const char* msg, size_t len, void* userData) {
|
|
if (auto* resp = static_cast<StorageResponse*>(userData)) {
|
|
resp->setResult(callerRet, msg, len);
|
|
}
|
|
}
|