#include "storage_client.hpp" #include #include #include #include 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 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 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 lock(mtx_); return status_; } std::string StorageResponse::data() const { std::lock_guard lock(mtx_); return result_; } size_t StorageResponse::progressCount() const { std::lock_guard lock(mtx_); return progressCount_; } std::string StorageResponse::lastProgress() const { std::lock_guard 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& peerAddresses) { std::vector 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(userData)) { resp->setResult(callerRet, msg, len); } }