diff --git a/.cargo/config.toml b/.cargo/config.toml deleted file mode 100644 index 1ca7145..0000000 --- a/.cargo/config.toml +++ /dev/null @@ -1,4 +0,0 @@ -[alias] -test-unit = "test --lib" -test-integration = "test --test mod" -test-doc = "test --doc" diff --git a/.gitignore b/.gitignore index d96c024..35ac20e 100644 --- a/.gitignore +++ b/.gitignore @@ -32,7 +32,6 @@ src/ffi/bindings.rs # Test output /tests/output/ -/examples/output/ # Environment files .env @@ -47,4 +46,4 @@ src/ffi/bindings.rs *.debug # LLM -.kilocode \ No newline at end of file +.kilocode diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 37e6c20..0000000 --- a/.gitmodules +++ /dev/null @@ -1,4 +0,0 @@ -[submodule "vendor/nim-codex"] - path = vendor/nim-codex - url = https://github.com/codex-storage/nim-codex - branch = master diff --git a/Cargo.toml b/Cargo.toml index 428e8f3..07fd909 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,16 +1,23 @@ [package] -name = "codex-bindings" -version = "0.1.3" +name = "storage-bindings" +version = "0.2.0" edition = "2021" -description = "Rust bindings for Codex, the Decentralized Durability Engine" +description = "Rust bindings for Storage, the Decentralized Durability Engine" license = "MIT" -repository = "https://github.com/nipsysdev/codex-rust-bindings" -homepage = "https://codex.storage" -keywords = ["codex", "storage", "p2p", "distributed"] +repository = "https://github.com/nipsysdev/storage-rust-bindings" +homepage = "https://logos.co/tech-stack" +keywords = ["storage", "p2p", "distributed"] categories = ["api-bindings", "network-programming"] +# Prebuilt binary version pinning +# Set this to a specific version from https://github.com/nipsysdev/logos-storage-nim-bin/releases +# Format: - (e.g., "master-60861d6a") +# Leave empty or comment out to use the latest release +[package.metadata.prebuilt] +libstorage = "master-1acedcf" + [lib] -name = "codex_bindings" +name = "storage_bindings" crate-type = ["cdylib", "rlib"] [dependencies] @@ -18,9 +25,6 @@ libc = "0.2" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" thiserror = "2.0" -log = "0.4" -chrono = { version = "0.4", features = ["serde"] } -once_cell = "1.21" bytesize = "2.1" futures = "0.3" @@ -31,16 +35,25 @@ optional = true [build-dependencies] bindgen = "0.72" -pkg-config = "0.3" +reqwest = { version = "0.12", features = ["blocking", "json"] } +flate2 = "1.0" +tar = "0.4" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +sha2 = "0.10" +hex = "0.4" cc = "1.2" +dirs = "6.0" [dev-dependencies] tempfile = "3.23" -tokio-test = "0.4" env_logger = "0.10" -tokio = { version = "1", features = ["macros", "io-util", "rt-multi-thread"] } +tokio = { version = "1", features = [ + "macros", + "io-util", + "rt-multi-thread", + "time", +] } [features] default = ["tokio"] -static-linking = [] -dynamic-linking = [] diff --git a/README.md b/README.md index e7495f2..101f8eb 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Codex Rust Bindings +# Storage Rust Bindings -This repository provides Rust bindings for the Codex library, enabling seamless integration with Rust projects. +This repository provides Rust bindings for the Storage library, enabling seamless integration with Rust projects. ## Usage @@ -8,80 +8,145 @@ Include in your Cargo project: ```toml [dependencies] -codex-bindings = "0.1.3" +storage-bindings = "0.2.0" ``` -To learn how to use those bindings, take a look at the [example project](https://github.com/nipsysdev/example-codex-rust-bindings) or the [integration tests](./tests/). +To learn how to use those bindings, take a look at the [example project](https://github.com/nipsysdev/example-storage-rust-bindings) or the [integration tests](./tests/) directory. ## Building -### Requirements +Building will automatically: -This crate automatically builds the required libcodex library during compilation, so you don't need to install nim-codex separately. However, you will need: +1. Fetch the latest prebuilt libstorage binary for your platform from GitHub +2. Generate Rust bindings and compile the crate -- **Rust and Cargo** -- **Git** -- **Make** -- **C compiler** +**Note**: The first build will download the prebuilt binary (~50MB). Subsequent builds will use the cached version. -Building will automatically: +## Caching -1. Clone the nim-codex repository and it's submodules -2. Build the Nim compiler from source -3. Build libcodex with the Nim compiler -4. Generate Rust bindings and compile the crate +Prebuilt binaries are automatically cached to improve build performance and reduce network usage. -**Note**: The first build may take 10-20 minutes as it needs to build the Nim compiler from source. Subsequent builds will be much faster. +### Cache Location -### Building from source +Prebuilt binaries are cached in a platform-specific location: + +- **Linux**: `~/.cache/storage-bindings/` +- **macOS**: `~/Library/Caches/storage-bindings/` +- **Windows**: `%LOCALAPPDATA%\storage-bindings\cache\` + +The cache is organized by version and platform: + +``` +~/.cache/storage-bindings/ +├── master-1acedcf/ +│ ├── linux-amd64/ +│ │ ├── libstorage.a +│ │ ├── libstorage.h +│ │ └── SHA256SUMS.txt +│ └── darwin-arm64/ +│ └── ... +└── master-2b3d4e5/ + └── ... +``` + +### Managing the Cache + +#### Force Re-download + +To force a fresh download without clearing the cache: ```bash -cargo build --release -# or, for debug -cargo build +STORAGE_BINDINGS_FORCE_DOWNLOAD=1 cargo build ``` -### Other Cargo Commands +#### Clean Entire Cache + +To remove all cached binaries: ```bash -# Run all tests -cargo test +# Linux/macOS +rm -rf ~/.cache/storage-bindings/ + +# Windows +rmdir /s /q %LOCALAPPDATA%\storage-bindings\cache -# Run unit tests -cargo test-unit +# Or using the build script +STORAGE_BINDINGS_CLEAN_CACHE=1 cargo build +``` -# Run integration tests -cargo test-integration +### Supported Platforms -# Run doctests -cargo test-doc +- Linux x86_64 (x86_64-unknown-linux-gnu) +- Linux ARM64 (aarch64-unknown-linux-gnu) + +### Libstorage Version Pinning + +**Option 1: Cargo.toml metadata** + +Add to your `Cargo.toml`: + +```toml +[package.metadata.prebuilt] +libstorage = "master-60861d6a" ``` -## Linking Modes +**Option 2: Environment variable (for local overrides)** -This crate supports two linking modes via Cargo features: +```bash +export LOGOS_STORAGE_VERSION=master-60861d6a +cargo build +``` -### Dynamic Linking (Default) +Available versions can be found at: https://github.com/nipsysdev/logos-storage-nim-bin/releases + +### Building from source ```bash +cargo build --release +# or, for debug cargo build -# or explicitly -cargo build --features dynamic-linking ``` -### Static Linking +### Building using local libraries + +To use locally built libraries instead of downloading from GitHub, set the `STORAGE_BINDINGS_LOCAL_LIBS` environment variable to the path of the dist folder: ```bash -cargo build --features static-linking +export STORAGE_BINDINGS_LOCAL_LIBS=/path/to/logos-storage-nim-bin/dist/master-50bd1839-linux-amd64 +cargo build ``` -### In your Cargo.toml +### Testing -```toml -[dependencies] -codex-bindings = { version = "0.1.3", features = ["static-linking"] } +The library includes comprehensive integration tests that demonstrate all major functionality. + +#### Running All Tests + +```bash +# Run all tests (unit tests + integration tests) +cargo test ``` +#### Running Specific Tests + +```bash +# Run only unit tests +cargo test --lib + +# Run only integration tests +cargo test --test $test_name +``` + +#### Available Integration Tests + +- **basic_usage**: Demonstrates basic upload/download functionality +- **chunk_operations**: Shows chunk-based upload and download operations +- **debug_operations**: Demonstrates debug operations and logging +- **p2p_networking**: Shows P2P networking operations +- **storage_management**: Demonstrates storage management operations +- **two_node_network**: Shows two-node network setup and data transfer +- **thread_safe_tests**: Tests thread-safe node lifecycle and concurrent operations + ## License [MIT](./LICENSE) diff --git a/build.rs b/build.rs index f9bfd96..5c601d9 100644 --- a/build.rs +++ b/build.rs @@ -1,335 +1,64 @@ use std::env; use std::path::PathBuf; -use std::process::Command; -fn check_required_tools() { - let tools = ["git", "make"]; - for tool in &tools { - if let Err(_) = Command::new(tool).arg("--version").output() { - panic!( - "Required tool '{}' is not installed or not in PATH. Please install it and try again.", - tool - ); - } - } - println!("All required tools are available"); -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum LinkingMode { - Static, - Dynamic, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -enum SourceMode { - Submodule, - Cloned, -} - -fn determine_linking_mode() -> LinkingMode { - let static_enabled = cfg!(feature = "static-linking"); - let dynamic_enabled = cfg!(feature = "dynamic-linking"); - - match (static_enabled, dynamic_enabled) { - (true, false) => LinkingMode::Static, - (false, true) => LinkingMode::Dynamic, - (false, false) => LinkingMode::Dynamic, - (true, true) => { - panic!("Cannot enable both 'static-linking' and 'dynamic-linking' features simultaneously. Please choose one."); - } - } -} - -fn determine_source_mode() -> SourceMode { - if env::var("CODEX_USE_CLONED").is_ok() { - println!("CODEX_USE_CLONED detected, using cloned mode"); - return SourceMode::Cloned; - } - - let vendor_submodule = PathBuf::from("vendor/nim-codex"); - if vendor_submodule.join(".git").exists() && vendor_submodule.join("codex").exists() { - println!("Using vendor/nim-codex submodule"); - SourceMode::Submodule - } else { - println!("Vendor submodule not found or incomplete, using cloned mode"); - SourceMode::Cloned - } -} - -fn get_nim_codex_dir() -> PathBuf { - let source_mode = determine_source_mode(); - let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); - - match source_mode { - SourceMode::Submodule => PathBuf::from("vendor/nim-codex"), - SourceMode::Cloned => { - let cloned_dir = out_dir.join("nim-codex"); - if !cloned_dir.exists() { - clone_nim_codex(&cloned_dir); - } else { - println!("Using previously cloned nim-codex in OUT_DIR"); - } - cloned_dir - } - } -} - -fn clone_nim_codex(target_dir: &PathBuf) { - println!("Cloning nim-codex repository..."); - - let status = Command::new("git") - .args(&[ - "clone", - "--branch", - "master", - "--recurse-submodules", - "https://github.com/codex-storage/nim-codex", - &target_dir.to_string_lossy(), - ]) - .status() - .expect("Failed to execute git clone. Make sure git is installed and in PATH."); - - if !status.success() { - panic!( - "Failed to clone nim-codex repository from https://github.com/codex-storage/nim-codex. \ - Please check your internet connection and repository access." - ); - } - - println!("Successfully cloned nim-codex"); -} - -fn build_libcodex_static(nim_codex_dir: &PathBuf) { - println!("Building libcodex with static linking..."); - - let codex_params = env::var("CODEX_LIB_PARAMS").unwrap_or_default(); - - let mut make_cmd = Command::new("make"); - make_cmd.args(&[ - "-C", - &nim_codex_dir.to_string_lossy(), - "STATIC=1", - "libcodex", - ]); - - if !codex_params.is_empty() { - make_cmd.env("CODEX_LIB_PARAMS", &codex_params); - } - - make_cmd.env("V", "1"); - make_cmd.env("USE_SYSTEM_NIM", "0"); - - println!("Running make command to build libcodex (this may take several minutes)..."); - - let output = make_cmd - .output() - .expect("Failed to execute make command. Make sure make is installed and in PATH."); - - if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - let stdout = String::from_utf8_lossy(&output.stdout); - - eprintln!("Build failed with stderr:"); - eprintln!("{}", stderr); - eprintln!("Build stdout:"); - eprintln!("{}", stdout); - - panic!( - "Failed to build libcodex with static linking. This could be due to:\n\ - 1. Missing build dependencies (C compiler, make, git)\n\ - 2. Network issues during repository cloning\n\ - 3. Insufficient disk space or memory\n\ - 4. Build timeout in CI environments\n\ - \n\ - For troubleshooting, try building manually:\n\ - cd {:?}\n\ - make deps\n\ - make STATIC=1 libcodex", - nim_codex_dir - ); - } - - println!("Successfully built libcodex (static)"); -} - -fn build_libcodex_dynamic(nim_codex_dir: &PathBuf) { - println!("Building libcodex with dynamic linking..."); - - let codex_params = env::var("CODEX_LIB_PARAMS").unwrap_or_default(); - - let mut make_cmd = Command::new("make"); - make_cmd.args(&["-C", &nim_codex_dir.to_string_lossy(), "libcodex"]); - - if !codex_params.is_empty() { - make_cmd.env("CODEX_LIB_PARAMS", &codex_params); - } - - let status = make_cmd - .status() - .expect("Failed to execute make command. Make sure make is installed and in PATH."); - - if !status.success() { - panic!( - "Failed to build libcodex with dynamic linking. Please ensure:\n\ - 1. Nim compiler is installed and in PATH\n\ - 2. All build dependencies are available\n\ - 3. The nim-codex repository is complete and not corrupted" - ); - } +mod src_build; - println!("Successfully built libcodex (dynamic)"); -} - -fn ensure_libcodex(nim_codex_dir: &PathBuf, lib_dir: &PathBuf, linking_mode: LinkingMode) { - let lib_exists = match linking_mode { - LinkingMode::Static => lib_dir.join("libcodex.a").exists(), - LinkingMode::Dynamic => lib_dir.join("libcodex.so").exists(), - }; - - if lib_exists { - println!("libcodex already built, skipping build step"); - return; - } - - match linking_mode { - LinkingMode::Static => build_libcodex_static(nim_codex_dir), - LinkingMode::Dynamic => build_libcodex_dynamic(nim_codex_dir), - } -} - -fn link_static_library(nim_codex_dir: &PathBuf, _lib_dir: &PathBuf) { - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-libbacktrace/vendor/libbacktrace-upstream/.libs") - .display() - ); - - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-circom-compat/vendor/circom-compat-ffi/target/release") - .display() - ); - - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-nat-traversal/vendor/libnatpmp-upstream") - .display() - ); +fn main() { + println!("=== Starting build.rs ==="); + // Tell Cargo to rerun if environment variables change println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-nat-traversal/vendor/miniupnp/miniupnpc/build") - .display() + "cargo:rerun-if-env-changed={}", + src_build::cache::CLEAN_CACHE_ENV_VAR ); - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("vendor/nim-libbacktrace/install/usr/lib") - .display() + "cargo:rerun-if-env-changed={}", + src_build::cache::FORCE_DOWNLOAD_ENV_VAR ); - println!( - "cargo:rustc-link-search=native={}", - nim_codex_dir - .join("nimcache/release/libcodex/vendor_leopard") - .display() + "cargo:rerun-if-env-changed={}", + src_build::version::STORAGE_VERSION_VAR ); - println!("cargo:rustc-link-arg=-Wl,--whole-archive"); - - println!("cargo:rustc-link-lib=static=backtrace"); - println!("cargo:rustc-link-lib=static=circom_compat_ffi"); - println!("cargo:rustc-link-lib=static=natpmp"); - println!("cargo:rustc-link-lib=static=miniupnpc"); - println!("cargo:rustc-link-lib=static=backtracenim"); - println!("cargo:rustc-link-lib=static=libleopard"); - - println!("cargo:rustc-link-lib=static=codex"); - - println!("cargo:rustc-link-arg=-Wl,--no-whole-archive"); - - println!("cargo:rustc-link-lib=stdc++"); - - println!("cargo:rustc-link-lib=dylib=gomp"); - - println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition"); - println!("cargo:rustc-link-arg=-Wl,--defsym=__rust_probestack=0"); - - println!("cargo:rustc-link-arg=-Wl,--defsym=cmdCount=0"); - println!("cargo:rustc-link-arg=-Wl,--defsym=cmdLine=0"); -} - -fn link_dynamic_library(lib_dir: &PathBuf) { - println!("cargo:rustc-link-lib=dylib=codex"); - - let lib_dir_abs = std::fs::canonicalize(lib_dir).unwrap_or_else(|_| lib_dir.to_path_buf()); - println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir_abs.display()); -} - -fn generate_bindings(nim_codex_dir: &PathBuf) { - let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); - let libcodex_header_path = nim_codex_dir.join("nimcache/release/libcodex/libcodex.h"); - - let mut builder = bindgen::Builder::default() - .header(libcodex_header_path.to_str().expect("Invalid path")) - .default_enum_style(bindgen::EnumVariation::Rust { - non_exhaustive: false, - }) - .generate_block(true) - .layout_tests(false) - .allowlist_function("codex_.*") - .allowlist_type("codex_.*") - .allowlist_var("codex_.*") - .allowlist_var("RET_.*") - .raw_line("#[allow(non_camel_case_types)]") - .raw_line("pub type CodexCallback = tyProc__crazOL9c5Gf8j9cqs2fd61EA;"); - - let nim_lib_path = nim_codex_dir.join("vendor/nimbus-build-system/vendor/Nim/lib"); - if nim_lib_path.exists() { - builder = builder.clang_arg(format!("-I{}", nim_lib_path.display())); + // Handle cache cleanup request + if src_build::cache::should_clean_cache() { + println!("\n=== Cleaning cache ==="); + src_build::cache::clean_cache().expect("Failed to clean cache"); + println!("=== Cache cleanup complete ==="); + return; } - let bindings = builder.generate().expect("Unable to generate bindings"); - - bindings - .write_to_file(out_path.join("bindings.rs")) - .expect("Couldn't write bindings!"); - - println!("cargo:rerun-if-changed={}", libcodex_header_path.display()); - println!("cargo:rerun-if-changed=vendor/libcodex.h"); -} - -fn main() { - check_required_tools(); - - let linking_mode = determine_linking_mode(); - let nim_codex_dir = get_nim_codex_dir(); - - let lib_dir = nim_codex_dir.join("build"); - let _include_dir = nim_codex_dir.join("nimcache/release/libcodex"); + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let target = env::var("TARGET").unwrap_or_default(); println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=vendor/nim-codex"); - println!("cargo:rerun-if-changed=vendor/libcodex.h"); - - match linking_mode { - LinkingMode::Static => { - ensure_libcodex(&nim_codex_dir, &lib_dir, LinkingMode::Static); - link_static_library(&nim_codex_dir, &lib_dir); - } - LinkingMode::Dynamic => { - ensure_libcodex(&nim_codex_dir, &lib_dir, LinkingMode::Dynamic); - link_dynamic_library(&lib_dir); - } - } - - println!("cargo:rustc-link-search=native={}", lib_dir.display()); - - generate_bindings(&nim_codex_dir); + println!("Build configuration:"); + println!(" OUT_DIR: {}", out_dir.display()); + println!(" TARGET: {}", target); + println!(" HOST: {}", env::var("HOST").unwrap_or_default()); + println!(" PROFILE: {}", env::var("PROFILE").unwrap_or_default()); + println!(" OPT_LEVEL: {}", env::var("OPT_LEVEL").unwrap_or_default()); + + // Step 1: Compile cmdline symbols to provide missing Nim symbols + println!("\n=== Step 1: Compiling cmdline symbols ==="); + src_build::cmdline::compile_cmdline_symbols(); + println!("✓ Cmdline symbols compiled successfully"); + + // Step 2: Ensure prebuilt binary is available + println!("\n=== Step 2: Ensuring prebuilt binary ==="); + let lib_dir = src_build::prebuilt::ensure_prebuilt_binary(&out_dir, &target) + .expect("Failed to download/extract prebuilt binary"); + println!("✓ Prebuilt binary available at: {}", lib_dir.display()); + + // Step 3: Generate bindings + println!("\n=== Step 3: Generating bindings ==="); + src_build::bindings::generate_bindings(&lib_dir); + println!("✓ Bindings generated successfully"); + + // Step 4: Link against prebuilt library + println!("\n=== Step 4: Linking against prebuilt library ==="); + src_build::linker::link_prebuilt_library(&lib_dir); + println!("✓ Linking configuration complete"); + + println!("\n=== build.rs completed successfully ==="); } diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..1a4728d --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,48 @@ +# EXAMPLE USAGE: +# +# Refer for explanation to following link: +# https://lefthook.dev/configuration/ +# +# pre-push: +# jobs: +# - name: packages audit +# tags: +# - frontend +# - security +# run: yarn audit +# +# - name: gems audit +# tags: +# - backend +# - security +# run: bundle audit +# +# pre-commit: +# parallel: true +# jobs: +# - run: yarn eslint {staged_files} +# glob: "*.{js,ts,jsx,tsx}" +# +# - name: rubocop +# glob: "*.rb" +# exclude: +# - config/application.rb +# - config/routes.rb +# run: bundle exec rubocop --force-exclusion {all_files} +# +# - name: govet +# files: git ls-files -m +# glob: "*.go" +# run: go vet {files} +# +# - script: "hello.js" +# runner: node +# +# - script: "hello.go" +# runner: go run +pre-commit: + parallel: true + jobs: + - run: cargo check --workspace + - run: cargo fmt -- --check + - run: cargo clippy diff --git a/mise.toml b/mise.toml new file mode 100644 index 0000000..2161fa2 --- /dev/null +++ b/mise.toml @@ -0,0 +1,3 @@ +[tools] +lefthook = "latest" +rust = "1.92.0" diff --git a/src/callback.rs b/src/callback.rs index 3939785..2958301 100644 --- a/src/callback.rs +++ b/src/callback.rs @@ -1,4 +1,4 @@ -use crate::error::{CodexError, Result}; +use crate::error::{Result, StorageError}; use crate::ffi::{c_str_to_string, CallbackReturn}; use libc::{c_char, c_int, c_void, size_t}; use std::collections::HashMap; @@ -7,7 +7,10 @@ use std::task::{Context, Poll, Waker}; use std::thread; use std::time::Duration; -static LIBCODEX_MUTEX: Mutex<()> = Mutex::new(()); +/// Type alias for the progress callback function type +type ProgressCallback = Box) + Send>; + +static LIBSTORAGE_MUTEX: Mutex<()> = Mutex::new(()); static CALLBACK_REGISTRY: LazyLock>>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -16,11 +19,17 @@ static NEXT_CALLBACK_ID: LazyLock> = LazyLock::new(|| Mutex::new(1)); pub struct CallbackContext { result: Mutex>>, waker: Mutex>, - progress_callback: Mutex) + Send>>>, + progress_callback: Mutex>, completed: Mutex, id: u64, } +impl Default for CallbackContext { + fn default() -> Self { + Self::new() + } +} + impl CallbackContext { pub fn new() -> Self { let id = { @@ -61,7 +70,17 @@ impl CallbackContext { } } - pub unsafe fn handle_callback(&self, ret: i32, msg: *mut c_char, len: size_t) { + /// Handles a callback from the C library + /// + /// # Safety + /// + /// The `msg` pointer must be either: + /// - A valid pointer to a null-terminated C string (for Ok/Error callbacks) + /// - A valid pointer to a byte array of length `len` (for Progress callbacks) + /// - A null pointer (which will be handled appropriately) + /// + /// The memory pointed to by `msg` must remain valid for the duration of this call. + pub unsafe fn handle_callback(&self, ret: i32, msg: *const c_char, len: size_t) { match CallbackReturn::from(ret) { CallbackReturn::Ok => { let message = unsafe { @@ -89,7 +108,7 @@ impl CallbackContext { } }; - *self.result.lock().unwrap() = Some(Err(CodexError::library_error(message))); + *self.result.lock().unwrap() = Some(Err(StorageError::library_error(message))); *self.completed.lock().unwrap() = true; if let Some(waker) = self.waker.lock().unwrap().take() { @@ -124,7 +143,7 @@ impl CallbackContext { if let Some(result) = self.get_result() { result } else { - Err(CodexError::timeout("callback operation")) + Err(StorageError::timeout("callback operation")) } } } @@ -141,6 +160,12 @@ pub struct CallbackFuture { pub(crate) context: Arc, } +impl Default for CallbackFuture { + fn default() -> Self { + Self::new() + } +} + impl CallbackFuture { pub fn new() -> Self { let context = Arc::new(CallbackContext::new()); @@ -185,16 +210,35 @@ impl std::future::Future for CallbackFuture { unsafe impl Send for CallbackFuture {} -pub fn with_libcodex_lock(f: F) -> R +pub fn with_libstorage_lock(f: F) -> R where F: FnOnce() -> R, { - let _lock = LIBCODEX_MUTEX.lock().unwrap(); + let _lock = LIBSTORAGE_MUTEX.lock().unwrap(); f() } +/// C callback function that is called from the libstorage library +/// +/// # Safety +/// +/// The `msg` pointer must be either: +/// - A valid pointer to a null-terminated C string (for Ok/Error callbacks) +/// - A valid pointer to a byte array of length `len` (for Progress callbacks) +/// - A null pointer (which will be handled appropriately) +/// +/// The `resp` pointer must be either: +/// - A valid pointer to a u64 callback ID that was previously registered +/// - A null pointer (which will cause the function to return early) +/// +/// The memory pointed to by `msg` and `resp` must remain valid for the duration of this call. #[no_mangle] -pub unsafe extern "C" fn c_callback(ret: c_int, msg: *mut c_char, len: size_t, resp: *mut c_void) { +pub unsafe extern "C" fn c_callback( + ret: c_int, + msg: *const c_char, + len: size_t, + resp: *mut c_void, +) { if resp.is_null() { return; } @@ -220,17 +264,6 @@ pub unsafe extern "C" fn c_callback(ret: c_int, msg: *mut c_char, len: size_t, r mod tests { use super::*; use std::sync::atomic::{AtomicBool, Ordering}; - use std::task::Wake; - - struct TestWaker { - woken: AtomicBool, - } - - impl Wake for TestWaker { - fn wake(self: Arc) { - self.woken.store(true, Ordering::SeqCst); - } - } #[test] fn test_callback_context_creation() { @@ -259,12 +292,12 @@ mod tests { assert!(result.is_err()); match result { - Err(CodexError::LibraryError { message, .. }) => { + Err(StorageError::LibraryError { message, .. }) => { assert_eq!(message, "Unknown error"); } other => { assert!( - matches!(other, Err(CodexError::LibraryError { .. })), + matches!(other, Err(StorageError::LibraryError { .. })), "Expected LibraryError with message 'Unknown error', got: {:?}", other ); diff --git a/src/debug/mod.rs b/src/debug/mod.rs index d806fca..7157ddf 100644 --- a/src/debug/mod.rs +++ b/src/debug/mod.rs @@ -1,6 +1,6 @@ -//! Debug operations for Codex +//! Debug operations for Storage //! -//! This module provides comprehensive debugging and diagnostics functionality for Codex nodes. +//! This module provides comprehensive debugging and diagnostics functionality for Storage nodes. //! It includes operations for getting node information, updating log levels, and peer debugging. //! //! ## Node Debugging diff --git a/src/debug/node.rs b/src/debug/node.rs index abc4cee..3a0b565 100644 --- a/src/debug/node.rs +++ b/src/debug/node.rs @@ -1,7 +1,7 @@ -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_debug, codex_log_level, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_debug, storage_log_level, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use libc::c_void; use serde::{Deserialize, Serialize}; @@ -117,15 +117,15 @@ impl Default for DebugInfo { } } -pub async fn debug(node: &CodexNode) -> Result { +pub async fn debug(node: &StorageNode) -> Result { let node = node.clone(); tokio::task::spawn_blocking(move || { let future = CallbackFuture::new(); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_debug( + storage_debug( ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -134,20 +134,21 @@ pub async fn debug(node: &CodexNode) -> Result { }); if result != 0 { - return Err(CodexError::library_error("Failed to get debug info")); + return Err(StorageError::library_error("Failed to get debug info")); } let debug_json = future.wait()?; - let debug_info: DebugInfo = serde_json::from_str(&debug_json) - .map_err(|e| CodexError::library_error(format!("Failed to parse debug info: {}", e)))?; + let debug_info: DebugInfo = serde_json::from_str(&debug_json).map_err(|e| { + StorageError::library_error(format!("Failed to parse debug info: {}", e)) + })?; Ok(debug_info) }) .await? } -pub async fn update_log_level(node: &CodexNode, log_level: LogLevel) -> Result<()> { +pub async fn update_log_level(node: &StorageNode, log_level: LogLevel) -> Result<()> { let node = node.clone(); tokio::task::spawn_blocking(move || { @@ -155,9 +156,9 @@ pub async fn update_log_level(node: &CodexNode, log_level: LogLevel) -> Result<( let c_log_level = string_to_c_string(&log_level.to_string()); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_log_level( + storage_log_level( ctx as *mut _, c_log_level, Some(c_callback), @@ -171,7 +172,7 @@ pub async fn update_log_level(node: &CodexNode, log_level: LogLevel) -> Result<( } if result != 0 { - return Err(CodexError::library_error("Failed to update log level")); + return Err(StorageError::library_error("Failed to update log level")); } future.wait()?; diff --git a/src/debug/peer.rs b/src/debug/peer.rs index 3a4712b..cdb8fd6 100644 --- a/src/debug/peer.rs +++ b/src/debug/peer.rs @@ -2,10 +2,10 @@ //! //! This module contains peer-specific debugging operations. -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_peer_debug, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_peer_debug, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use crate::p2p::types::PeerRecord; use libc::c_void; @@ -13,19 +13,19 @@ use libc::c_void; /// /// # Arguments /// -/// * `node` - The Codex node to use +/// * `node` - The Storage node to use /// * `peer_id` - The peer ID to get debug information for /// /// # Returns /// /// Detailed peer record for debugging -pub async fn peer_debug(node: &CodexNode, peer_id: &str) -> Result { +pub async fn peer_debug(node: &StorageNode, peer_id: &str) -> Result { let node = node.clone(); let peer_id = peer_id.to_string(); tokio::task::spawn_blocking(move || { if peer_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_id", "Peer ID cannot be empty", )); @@ -34,13 +34,13 @@ pub async fn peer_debug(node: &CodexNode, peer_id: &str) -> Result { // Create a callback future for the operation let future = CallbackFuture::new(); - with_libcodex_lock(|| { + with_libstorage_lock(|| { let c_peer_id = string_to_c_string(&peer_id); // Call the C function with the context pointer directly let result = unsafe { node.with_ctx(|ctx| { - codex_peer_debug( + storage_peer_debug( ctx as *mut _, c_peer_id, Some(c_callback), @@ -55,7 +55,7 @@ pub async fn peer_debug(node: &CodexNode, peer_id: &str) -> Result { } if result != 0 { - return Err(CodexError::library_error("Failed to get peer debug info")); + return Err(StorageError::library_error("Failed to get peer debug info")); } Ok(()) @@ -66,7 +66,7 @@ pub async fn peer_debug(node: &CodexNode, peer_id: &str) -> Result { // Parse the peer JSON let peer: PeerRecord = serde_json::from_str(&peer_json).map_err(|e| { - CodexError::library_error(format!("Failed to parse peer debug info: {}", e)) + StorageError::library_error(format!("Failed to parse peer debug info: {}", e)) })?; Ok(peer) diff --git a/src/download/chunks.rs b/src/download/chunks.rs index e2948d2..18a65f2 100644 --- a/src/download/chunks.rs +++ b/src/download/chunks.rs @@ -1,13 +1,13 @@ -//! Chunk download operations for Codex +//! Chunk download operations for Storage //! //! This module provides functionality for downloading individual chunks of data -//! from the Codex network. Chunks are the basic unit of data transfer +//! from the Storage network. Chunks are the basic unit of data transfer //! and can be downloaded individually or as part of a larger download. -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_download_chunk, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_download_chunk, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use libc::c_void; use std::sync::{Arc, Mutex}; @@ -19,7 +19,7 @@ use std::sync::{Arc, Mutex}; /// /// # Arguments /// -/// * `node` - The Codex node to use for the download +/// * `node` - The Storage node to use for the download /// * `cid` - The content ID of the chunk to download /// /// # Returns @@ -31,13 +31,16 @@ use std::sync::{Arc, Mutex}; /// Returns an error if: /// - The CID is empty /// - The chunk download fails -pub async fn download_chunk(node: &CodexNode, cid: &str) -> Result> { +pub async fn download_chunk(node: &StorageNode, cid: &str) -> Result> { let node = node.clone(); let cid = cid.to_string(); tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } let chunk_data = Arc::new(Mutex::new(Vec::::new())); @@ -55,10 +58,11 @@ pub async fn download_chunk(node: &CodexNode, cid: &str) -> Result> { let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { let ctx = node.ctx(); let c_cid = string_to_c_string(&cid); - let result = codex_download_chunk(ctx as *mut _, c_cid, Some(c_callback), context_ptr); + let result = + storage_download_chunk(ctx as *mut _, c_cid, Some(c_callback), context_ptr); free_c_string(c_cid); @@ -66,7 +70,7 @@ pub async fn download_chunk(node: &CodexNode, cid: &str) -> Result> { }); if result != 0 { - return Err(CodexError::download_error("Failed to download chunk")); + return Err(StorageError::download_error("Failed to download chunk")); } future.wait()?; @@ -85,7 +89,7 @@ pub async fn download_chunk(node: &CodexNode, cid: &str) -> Result> { /// /// # Arguments /// -/// * `node` - The Codex node to use for the download +/// * `node` - The Storage node to use for the download /// * `cids` - A vector of content IDs to download /// /// # Returns @@ -95,7 +99,7 @@ pub async fn download_chunk(node: &CodexNode, cid: &str) -> Result> { /// # Errors /// /// Returns an error if any chunk download fails -pub async fn download_chunks(node: &CodexNode, cids: Vec) -> Result>> { +pub async fn download_chunks(node: &StorageNode, cids: Vec) -> Result>> { let node = node.clone(); let futures: Vec<_> = cids @@ -114,7 +118,7 @@ pub async fn download_chunks(node: &CodexNode, cids: Vec) -> Result chunks.push(chunk), Err((index, e)) => { - return Err(CodexError::download_error(format!( + return Err(StorageError::download_error(format!( "Failed to download chunk {}: {}", index, e ))); @@ -133,7 +137,7 @@ pub async fn download_chunks(node: &CodexNode, cids: Vec) -> Result) -> Result( - node: &CodexNode, + node: &StorageNode, cid: &str, progress_callback: F, ) -> Result<()> @@ -160,7 +164,10 @@ where tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } let future = CallbackFuture::new(); @@ -174,10 +181,11 @@ where let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { let ctx = node.ctx(); let c_cid = string_to_c_string(&cid); - let result = codex_download_chunk(ctx as *mut _, c_cid, Some(c_callback), context_ptr); + let result = + storage_download_chunk(ctx as *mut _, c_cid, Some(c_callback), context_ptr); free_c_string(c_cid); @@ -185,7 +193,7 @@ where }); if result != 0 { - return Err(CodexError::download_error("Failed to download chunk")); + return Err(StorageError::download_error("Failed to download chunk")); } future.wait()?; diff --git a/src/download/manifest.rs b/src/download/manifest.rs index 01c03a7..be76fce 100644 --- a/src/download/manifest.rs +++ b/src/download/manifest.rs @@ -1,28 +1,31 @@ -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; use crate::download::types::Manifest; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_download_manifest, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_download_manifest, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use libc::c_void; -pub async fn download_manifest(node: &CodexNode, cid: &str) -> Result { +pub async fn download_manifest(node: &StorageNode, cid: &str) -> Result { let node = node.clone(); let cid = cid.to_string(); tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } let future = CallbackFuture::new(); let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_cid = string_to_c_string(&cid); let result = - codex_download_manifest(ctx as *mut _, c_cid, Some(c_callback), context_ptr); + storage_download_manifest(ctx as *mut _, c_cid, Some(c_callback), context_ptr); free_c_string(c_cid); @@ -31,13 +34,13 @@ pub async fn download_manifest(node: &CodexNode, cid: &str) -> Result }); if result != 0 { - return Err(CodexError::download_error("Failed to download manifest")); + return Err(StorageError::download_error("Failed to download manifest")); } let manifest_json = future.wait()?; let manifest: Manifest = serde_json::from_str(&manifest_json) - .map_err(|e| CodexError::library_error(format!("Failed to parse manifest: {}", e)))?; + .map_err(|e| StorageError::library_error(format!("Failed to parse manifest: {}", e)))?; Ok(manifest) }) diff --git a/src/download/mod.rs b/src/download/mod.rs index 8deae3e..e399227 100644 --- a/src/download/mod.rs +++ b/src/download/mod.rs @@ -1,6 +1,6 @@ -//! Download operations for Codex +//! Download operations for Storage //! -//! This module provides comprehensive download functionality for the Codex distributed storage network. +//! This module provides comprehensive download functionality for the Storage distributed storage network. //! It supports both high-level and low-level download operations, streaming downloads, and manifest handling. //! //! ## High-Level Operations diff --git a/src/download/session.rs b/src/download/session.rs index 98c634e..90d4775 100644 --- a/src/download/session.rs +++ b/src/download/session.rs @@ -1,24 +1,26 @@ -//! Download session management for Codex +//! Download session management for Storage //! //! This module provides low-level session management operations for downloads. //! These functions handle the lifecycle of download sessions including initialization //! and cancellation. -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; use crate::download::types::DownloadOptions; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_download_cancel, codex_download_init, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::error::{Result, StorageError}; +use crate::ffi::{ + free_c_string, storage_download_cancel, storage_download_init, string_to_c_string, +}; +use crate::node::lifecycle::StorageNode; use libc::c_void; /// Initialize a download session /// /// Creates a new download session for the specified content ID (CID) with the given options. -/// This prepares the node to download content from the Codex network. +/// This prepares the node to download content from the Storage network. /// /// # Arguments /// -/// * `node` - The Codex node to use for the download +/// * `node` - The Storage node to use for the download /// * `cid` - The content ID to download /// * `options` - Download configuration options /// @@ -32,14 +34,17 @@ use libc::c_void; /// - The CID is empty /// - The options are invalid /// - The download initialization fails -pub async fn download_init(node: &CodexNode, cid: &str, options: &DownloadOptions) -> Result<()> { +pub async fn download_init(node: &StorageNode, cid: &str, options: &DownloadOptions) -> Result<()> { let node = node.clone(); let cid = cid.to_string(); let options = options.clone(); tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } options.validate()?; @@ -49,10 +54,10 @@ pub async fn download_init(node: &CodexNode, cid: &str, options: &DownloadOption let chunk_size = options.chunk_size.unwrap_or(1024 * 1024); let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_cid = string_to_c_string(&cid); - let result = codex_download_init( + let result = storage_download_init( ctx as *mut _, c_cid, chunk_size, @@ -68,7 +73,9 @@ pub async fn download_init(node: &CodexNode, cid: &str, options: &DownloadOption }); if result != 0 { - return Err(CodexError::download_error("Failed to initialize download")); + return Err(StorageError::download_error( + "Failed to initialize download", + )); } future.wait()?; @@ -85,7 +92,7 @@ pub async fn download_init(node: &CodexNode, cid: &str, options: &DownloadOption /// /// # Arguments /// -/// * `node` - The Codex node used for the download +/// * `node` - The Storage node used for the download /// * `cid` - The content ID of the download to cancel /// /// # Returns @@ -97,23 +104,27 @@ pub async fn download_init(node: &CodexNode, cid: &str, options: &DownloadOption /// Returns an error if: /// - The CID is empty /// - The cancellation fails -pub async fn download_cancel(node: &CodexNode, cid: &str) -> Result<()> { +pub async fn download_cancel(node: &StorageNode, cid: &str) -> Result<()> { let node = node.clone(); let cid = cid.to_string(); tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } let future = CallbackFuture::new(); let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { let ctx = node.ctx(); let c_cid = string_to_c_string(&cid); - let result = codex_download_cancel(ctx as *mut _, c_cid, Some(c_callback), context_ptr); + let result = + storage_download_cancel(ctx as *mut _, c_cid, Some(c_callback), context_ptr); free_c_string(c_cid); @@ -121,7 +132,7 @@ pub async fn download_cancel(node: &CodexNode, cid: &str) -> Result<()> { }); if result != 0 { - return Err(CodexError::download_error("Failed to cancel download")); + return Err(StorageError::download_error("Failed to cancel download")); } future.wait()?; @@ -133,12 +144,15 @@ pub async fn download_cancel(node: &CodexNode, cid: &str) -> Result<()> { /// Synchronous version of download_init for internal use pub(crate) fn download_init_sync( - node: &CodexNode, + node: &StorageNode, cid: &str, options: &DownloadOptions, ) -> Result<()> { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } options.validate()?; @@ -148,10 +162,10 @@ pub(crate) fn download_init_sync( let chunk_size = options.chunk_size.unwrap_or(1024 * 1024); let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_cid = string_to_c_string(cid); - let result = codex_download_init( + let result = storage_download_init( ctx as *mut _, c_cid, chunk_size, @@ -167,7 +181,9 @@ pub(crate) fn download_init_sync( }); if result != 0 { - return Err(CodexError::download_error("Failed to initialize download")); + return Err(StorageError::download_error( + "Failed to initialize download", + )); } future.wait()?; diff --git a/src/download/stream.rs b/src/download/stream.rs index b909ce0..65da61e 100644 --- a/src/download/stream.rs +++ b/src/download/stream.rs @@ -1,28 +1,28 @@ -//! Stream download operations for Codex +//! Stream download operations for Storage //! -//! This module provides high-level streaming download functionality for the Codex network. +//! This module provides high-level streaming download functionality for the Storage network. //! It supports downloading content directly to files, writers, or custom destinations //! with progress tracking and verification. -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; use crate::download::session::download_init_sync; use crate::download::types::{DownloadOptions, DownloadResult, DownloadStreamOptions}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_download_stream, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_download_stream, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use libc::c_void; use std::io::Write; use std::sync::{Arc, Mutex}; /// Download content as a stream to various destinations /// -/// High-level function that downloads content from the Codex network and streams it +/// High-level function that downloads content from the Storage network and streams it /// to a file, writer, or custom callback. This function handles the complete download /// process including session management, progress tracking, and error handling. /// /// # Arguments /// -/// * `node` - The Codex node to use for the download +/// * `node` - The Storage node to use for the download /// * `cid` - The content ID to download /// * `options` - Stream download options including destination and configuration /// @@ -37,7 +37,7 @@ use std::sync::{Arc, Mutex}; /// - The options are invalid /// - The download fails for any reason pub async fn download_stream( - node: &CodexNode, + node: &StorageNode, cid: &str, options: DownloadStreamOptions, ) -> Result { @@ -47,7 +47,10 @@ pub async fn download_stream( tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } options.validate()?; @@ -62,7 +65,7 @@ pub async fn download_stream( match std::fs::File::create(filepath) { Ok(file) => Some(Arc::new(Mutex::new(Some(file)))), Err(e) => { - return Err(CodexError::Io(e)); + return Err(StorageError::Io(e)); } } } else { @@ -103,7 +106,7 @@ pub async fn download_stream( } } - if let Err(_) = tx_clone.send(chunk_bytes.to_vec()) { + if tx_clone.send(chunk_bytes.to_vec()).is_err() { eprintln!("Failed to send data to writer thread"); } } @@ -124,12 +127,12 @@ pub async fn download_stream( .and_then(|p| p.to_str()) .unwrap_or(""); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_cid = string_to_c_string(cid_str); let c_filepath = string_to_c_string(filepath_str); - let result = codex_download_stream( + let result = storage_download_stream( ctx as *mut _, c_cid, chunk_size, @@ -149,7 +152,7 @@ pub async fn download_stream( }); if result != 0 { - return Err(CodexError::download_error("Failed to download stream")); + return Err(StorageError::download_error("Failed to download stream")); } future.wait()?; @@ -194,7 +197,7 @@ pub async fn download_stream( /// /// # Arguments /// -/// * `node` - The Codex node to use for the download +/// * `node` - The Storage node to use for the download /// * `cid` - The content ID to download /// * `filepath` - The path where the file should be saved /// @@ -206,7 +209,7 @@ pub async fn download_stream( /// /// Returns an error if the download fails pub async fn download_to_file( - node: &CodexNode, + node: &StorageNode, cid: &str, filepath: &std::path::Path, ) -> Result { @@ -224,7 +227,7 @@ pub async fn download_to_file( /// /// # Arguments /// -/// * `node` - The Codex node to use for the download +/// * `node` - The Storage node to use for the download /// * `cid` - The content ID to download /// * `writer` - Any type that implements Write /// @@ -235,7 +238,11 @@ pub async fn download_to_file( /// # Errors /// /// Returns an error if the download fails -pub async fn download_to_writer(node: &CodexNode, cid: &str, writer: W) -> Result +pub async fn download_to_writer( + node: &StorageNode, + cid: &str, + writer: W, +) -> Result where W: Write + Send + 'static, { diff --git a/src/download/types.rs b/src/download/types.rs index 1ddcfbd..83f90de 100644 --- a/src/download/types.rs +++ b/src/download/types.rs @@ -1,6 +1,6 @@ //! Types for download operations -use crate::error::{CodexError, Result}; +use crate::error::{Result, StorageError}; use serde::{Deserialize, Serialize}; use std::io::Write; use std::path::PathBuf; @@ -160,12 +160,15 @@ impl DownloadOptions { /// Validate the download options pub fn validate(&self) -> Result<()> { if self.cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } if let Some(chunk_size) = self.chunk_size { if chunk_size == 0 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "chunk_size", "Chunk size must be greater than 0", )); @@ -174,7 +177,7 @@ impl DownloadOptions { if let Some(timeout) = self.timeout { if timeout == 0 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "timeout", "Timeout must be greater than 0", )); @@ -320,11 +323,14 @@ impl DownloadStreamOptions { /// Validate the download stream options pub fn validate(&self) -> Result<()> { if self.cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } if self.filepath.is_none() && self.writer.is_none() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "filepath/writer", "Either filepath or writer must be specified", )); @@ -332,7 +338,7 @@ impl DownloadStreamOptions { if let Some(chunk_size) = self.chunk_size { if chunk_size == 0 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "chunk_size", "Chunk size must be greater than 0", )); @@ -341,7 +347,7 @@ impl DownloadStreamOptions { if let Some(timeout) = self.timeout { if timeout == 0 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "timeout", "Timeout must be greater than 0", )); @@ -455,7 +461,7 @@ mod tests { assert_eq!(options.cid, "QmExample"); assert_eq!(options.chunk_size, Some(2048)); assert_eq!(options.timeout, Some(600)); - assert_eq!(options.verify, false); + assert!(!options.verify); } #[test] @@ -493,7 +499,7 @@ mod tests { assert_eq!(options.dataset_size, Some(1024)); assert!(!options.dataset_size_auto); assert_eq!(options.timeout, Some(600)); - assert_eq!(options.verify, false); + assert!(!options.verify); } #[test] diff --git a/src/error.rs b/src/error.rs index 5857359..7b47bfa 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,10 +1,10 @@ use thiserror::Error; -pub type Result = std::result::Result; +pub type Result = std::result::Result; #[derive(Error, Debug)] -pub enum CodexError { - #[error("Codex library error: {message}")] +pub enum StorageError { + #[error("Storage library error: {message}")] LibraryError { message: String }, #[error("Node operation failed: {operation} - {message}")] @@ -50,157 +50,162 @@ pub enum CodexError { JoinError(#[from] tokio::task::JoinError), } -impl CodexError { +impl StorageError { pub fn library_error(message: impl Into) -> Self { - CodexError::LibraryError { + StorageError::LibraryError { message: message.into(), } } pub fn node_error(operation: impl Into, message: impl Into) -> Self { - CodexError::NodeError { + StorageError::NodeError { operation: operation.into(), message: message.into(), } } pub fn upload_error(message: impl Into) -> Self { - CodexError::UploadError { + StorageError::UploadError { message: message.into(), } } pub fn download_error(message: impl Into) -> Self { - CodexError::DownloadError { + StorageError::DownloadError { message: message.into(), } } - pub fn storage_error(operation: impl Into, message: impl Into) -> Self { - CodexError::StorageError { + pub fn storage_operation_error( + operation: impl Into, + message: impl Into, + ) -> Self { + StorageError::StorageError { operation: operation.into(), message: message.into(), } } pub fn p2p_error(message: impl Into) -> Self { - CodexError::P2PError { + StorageError::P2PError { message: message.into(), } } pub fn config_error(message: impl Into) -> Self { - CodexError::ConfigError { + StorageError::ConfigError { message: message.into(), } } pub fn invalid_parameter(parameter: impl Into, message: impl Into) -> Self { - CodexError::InvalidParameter { + StorageError::InvalidParameter { parameter: parameter.into(), message: message.into(), } } pub fn timeout(operation: impl Into) -> Self { - CodexError::Timeout { + StorageError::Timeout { operation: operation.into(), } } pub fn cancelled(operation: impl Into) -> Self { - CodexError::Cancelled { + StorageError::Cancelled { operation: operation.into(), } } pub fn null_pointer(context: impl Into) -> Self { - CodexError::NullPointer { + StorageError::NullPointer { context: context.into(), } } } -pub fn from_c_error(code: i32, message: &str) -> CodexError { +pub fn from_c_error(code: i32, message: &str) -> StorageError { match code { - 0 => CodexError::library_error(format!("Unexpected success with message: {}", message)), - 1 => CodexError::library_error(message), - _ => CodexError::library_error(format!("Unknown error code {}: {}", code, message)), + 0 => StorageError::library_error(format!("Unexpected success with message: {}", message)), + 1 => StorageError::library_error(message), + _ => StorageError::library_error(format!("Unknown error code {}: {}", code, message)), } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_error_creation() { - let err = CodexError::library_error("Test error"); - assert!(matches!(err, CodexError::LibraryError { .. })); - - let err = CodexError::node_error("start", "Failed to start"); - assert!(matches!(err, CodexError::NodeError { .. })); - - let err = CodexError::upload_error("Upload failed"); - assert!(matches!(err, CodexError::UploadError { .. })); - } - - #[test] - fn test_error_display() { - let err = CodexError::library_error("Test error"); - assert_eq!(err.to_string(), "Codex library error: Test error"); - - let err = CodexError::node_error("start", "Failed to start"); - assert_eq!( - err.to_string(), - "Node operation failed: start - Failed to start" - ); - } -} - -impl Clone for CodexError { +impl Clone for StorageError { fn clone(&self) -> Self { match self { - CodexError::LibraryError { message } => CodexError::LibraryError { + StorageError::LibraryError { message } => StorageError::LibraryError { message: message.clone(), }, - CodexError::NodeError { operation, message } => CodexError::NodeError { + StorageError::NodeError { operation, message } => StorageError::NodeError { operation: operation.clone(), message: message.clone(), }, - CodexError::UploadError { message } => CodexError::UploadError { + StorageError::UploadError { message } => StorageError::UploadError { message: message.clone(), }, - CodexError::DownloadError { message } => CodexError::DownloadError { + StorageError::DownloadError { message } => StorageError::DownloadError { message: message.clone(), }, - CodexError::StorageError { operation, message } => CodexError::StorageError { + StorageError::StorageError { operation, message } => StorageError::StorageError { operation: operation.clone(), message: message.clone(), }, - CodexError::P2PError { message } => CodexError::P2PError { - message: message.clone(), - }, - CodexError::ConfigError { message } => CodexError::ConfigError { + StorageError::P2PError { message } => StorageError::P2PError { message: message.clone(), }, - CodexError::InvalidParameter { parameter, message } => CodexError::InvalidParameter { - parameter: parameter.clone(), + StorageError::ConfigError { message } => StorageError::ConfigError { message: message.clone(), }, - CodexError::Timeout { operation } => CodexError::Timeout { + StorageError::InvalidParameter { parameter, message } => { + StorageError::InvalidParameter { + parameter: parameter.clone(), + message: message.clone(), + } + } + StorageError::Timeout { operation } => StorageError::Timeout { operation: operation.clone(), }, - CodexError::Cancelled { operation } => CodexError::Cancelled { + StorageError::Cancelled { operation } => StorageError::Cancelled { operation: operation.clone(), }, - CodexError::Io(_) => CodexError::library_error("I/O error"), - CodexError::Json(_) => CodexError::library_error("JSON error"), - CodexError::Utf8(_) => CodexError::library_error("UTF-8 error"), - CodexError::NullPointer { context } => CodexError::NullPointer { + StorageError::Io(_) => StorageError::library_error("I/O error"), + StorageError::Json(_) => StorageError::library_error("JSON error"), + StorageError::Utf8(_) => StorageError::library_error("UTF-8 error"), + StorageError::NullPointer { context } => StorageError::NullPointer { context: context.clone(), }, - CodexError::JoinError(_) => CodexError::library_error("Task join error"), + StorageError::JoinError(_) => StorageError::library_error("Task join error"), } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_error_creation() { + let err = StorageError::library_error("Test error"); + assert!(matches!(err, StorageError::LibraryError { .. })); + + let err = StorageError::node_error("start", "Failed to start"); + assert!(matches!(err, StorageError::NodeError { .. })); + + let err = StorageError::upload_error("Upload failed"); + assert!(matches!(err, StorageError::UploadError { .. })); + } + + #[test] + fn test_error_display() { + let err = StorageError::library_error("Test error"); + assert_eq!(err.to_string(), "Storage library error: Test error"); + + let err = StorageError::node_error("start", "Failed to start"); + assert_eq!( + err.to_string(), + "Node operation failed: start - Failed to start" + ); + } +} diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 95e9727..170e023 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -1,10 +1,11 @@ -//! FFI bindings for the libcodex C library +//! FFI bindings for the libstorage C library //! //! This module contains the low-level bindings generated by bindgen //! and provides safe wrappers around the C functions. // Include the generated bindings in a module to suppress warnings #[allow(non_camel_case_types)] +#[allow(non_snake_case)] mod generated { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } @@ -17,6 +18,14 @@ use std::ffi::CStr; use std::str::Utf8Error; /// Convert a C string to a Rust string +/// +/// # Safety +/// +/// The `ptr` must be either: +/// - A valid pointer to a null-terminated C string +/// - A null pointer (which will return an empty string) +/// +/// The memory pointed to by `ptr` must remain valid for the duration of this call. pub unsafe fn c_str_to_string(ptr: *const c_char) -> Result { if ptr.is_null() { return Ok(String::new()); @@ -32,6 +41,14 @@ pub fn string_to_c_string(s: &str) -> *mut c_char { } /// Free a C string created with string_to_c_string +/// +/// # Safety +/// +/// The `ptr` must be either: +/// - A valid pointer to a C string allocated by `string_to_c_string` +/// - A null pointer (which will be safely ignored) +/// +/// After calling this function, the pointer becomes invalid and must not be used. pub unsafe fn free_c_string(ptr: *mut c_char) { if !ptr.is_null() { let _ = std::ffi::CString::from_raw(ptr); @@ -59,8 +76,7 @@ impl From for CallbackReturn { } /// Callback function type - available from generated bindings -// The CodexCallback type alias is already available from the generated bindings - +// The StorageCallback type alias is already available from the generated bindings #[cfg(test)] mod tests { use super::*; diff --git a/src/lib.rs b/src/lib.rs index 37e927b..d81c0a2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,9 +17,9 @@ pub use download::{ DownloadOptions, DownloadProgress, DownloadResult, DownloadStreamOptions, }; -pub use error::{CodexError, Result}; +pub use error::{Result, StorageError}; -pub use node::{CodexConfig, CodexNode, LogFormat, LogLevel}; +pub use node::{LogFormat, LogLevel, StorageConfig, StorageNode}; pub use p2p::{ connect, connect_to_multiple, get_peer_id, get_peer_info, validate_addresses, validate_peer_id, diff --git a/src/node/config.rs b/src/node/config.rs index 20958e3..13d2d3c 100644 --- a/src/node/config.rs +++ b/src/node/config.rs @@ -1,15 +1,16 @@ -//! Node configuration structures for Codex +//! Node configuration structures for Storage -use crate::error::{CodexError, Result}; +use crate::error::{Result, StorageError}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; -/// Log level for the Codex node -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +/// Log level for the Storage node +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "lowercase")] pub enum LogLevel { Trace, Debug, + #[default] Info, Notice, Warn, @@ -17,12 +18,6 @@ pub enum LogLevel { Fatal, } -impl Default for LogLevel { - fn default() -> Self { - LogLevel::Info - } -} - impl std::fmt::Display for LogLevel { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -37,22 +32,17 @@ impl std::fmt::Display for LogLevel { } } -/// Log format for the Codex node -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +/// Log format for the Storage node +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "lowercase")] pub enum LogFormat { + #[default] Auto, Colors, NoColors, Json, } -impl Default for LogFormat { - fn default() -> Self { - LogFormat::Auto - } -} - impl std::fmt::Display for LogFormat { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -65,20 +55,15 @@ impl std::fmt::Display for LogFormat { } /// Repository kind for storage backend -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "lowercase")] pub enum RepoKind { + #[default] Fs, Sqlite, LevelDb, } -impl Default for RepoKind { - fn default() -> Self { - RepoKind::Fs - } -} - impl std::fmt::Display for RepoKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -89,9 +74,9 @@ impl std::fmt::Display for RepoKind { } } -/// Configuration for a Codex node +/// Configuration for a Storage node #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CodexConfig { +pub struct StorageConfig { /// Log level (default: INFO) #[serde(rename = "log-level", default, skip_serializing_if = "Option::is_none")] pub log_level: Option, @@ -124,7 +109,7 @@ pub struct CodexConfig { )] pub metrics_port: Option, - /// The directory where codex will store configuration and data + /// The directory where storage will store configuration and data #[serde(rename = "data-dir", default, skip_serializing_if = "Option::is_none")] pub data_dir: Option, @@ -172,7 +157,7 @@ pub struct CodexConfig { )] pub num_threads: Option, - /// Node agent string which is used as identifier in network (default: "Codex") + /// Node agent string which is used as identifier in network (default: "Storage") #[serde( rename = "agent-string", default, @@ -225,7 +210,7 @@ pub struct CodexConfig { pub log_file: Option, } -impl Default for CodexConfig { +impl Default for StorageConfig { fn default() -> Self { Self { log_level: Some(LogLevel::Info), @@ -241,7 +226,7 @@ impl Default for CodexConfig { bootstrap_nodes: vec![], max_peers: Some(160), num_threads: Some(0), - agent_string: Some("Codex".to_string()), + agent_string: Some("Storage".to_string()), repo_kind: Some(RepoKind::Fs), storage_quota: Some(20 * 1024 * 1024 * 1024), // 20 GiB block_ttl: Some(30 * 24 * 60 * 60), // 30 days in seconds @@ -254,7 +239,7 @@ impl Default for CodexConfig { } } -impl CodexConfig { +impl StorageConfig { /// Create a new configuration with minimal values (compatible with C library) pub fn new() -> Self { Self { @@ -428,12 +413,12 @@ impl CodexConfig { /// Convert the configuration to a JSON string pub fn to_json(&self) -> Result { - serde_json::to_string(self).map_err(CodexError::from) + serde_json::to_string(self).map_err(StorageError::from) } /// Create a configuration from a JSON string pub fn from_json(json: &str) -> Result { - serde_json::from_str(json).map_err(CodexError::from) + serde_json::from_str(json).map_err(StorageError::from) } } @@ -443,7 +428,7 @@ mod tests { #[test] fn test_default_config() { - let config = CodexConfig::default(); + let config = StorageConfig::default(); assert_eq!(config.log_level, Some(LogLevel::Info)); assert_eq!(config.log_format, Some(LogFormat::Auto)); assert_eq!(config.metrics_enabled, Some(false)); @@ -452,15 +437,15 @@ mod tests { #[test] fn test_config_builder() { - let config = CodexConfig::new() + let config = StorageConfig::new() .log_level(LogLevel::Debug) - .data_dir("/tmp/codex") + .data_dir("/tmp/storage") .storage_quota(1024 * 1024) // 1 MB .max_peers(100) .repo_kind(RepoKind::Sqlite); assert_eq!(config.log_level, Some(LogLevel::Debug)); - assert_eq!(config.data_dir, Some(PathBuf::from("/tmp/codex"))); + assert_eq!(config.data_dir, Some(PathBuf::from("/tmp/storage"))); assert_eq!(config.storage_quota, Some(1024 * 1024)); assert_eq!(config.max_peers, Some(100)); assert_eq!(config.repo_kind, Some(RepoKind::Sqlite)); @@ -468,7 +453,7 @@ mod tests { #[test] fn test_json_serialization_minimal_config() { - let config = CodexConfig::new(); + let config = StorageConfig::new(); let json_str = config.to_json().expect("Failed to serialize to JSON"); // Verify the JSON is valid @@ -483,7 +468,7 @@ mod tests { #[test] fn test_json_serialization_partial_config() { - let config = CodexConfig::new().log_level(LogLevel::Debug); + let config = StorageConfig::new().log_level(LogLevel::Debug); let json_str = config.to_json().expect("Failed to serialize to JSON"); // Verify the JSON is valid @@ -496,9 +481,9 @@ mod tests { #[test] fn test_json_serialization_full_config() { - let config = CodexConfig::new() + let config = StorageConfig::new() .log_level(LogLevel::Error) - .data_dir("/tmp/codex") + .data_dir("/tmp/storage") .storage_quota(1024 * 1024) .max_peers(50) .add_listen_addr("/ip4/127.0.0.1/tcp/8080") @@ -512,7 +497,7 @@ mod tests { // Full config assert_eq!(parsed["log-level"], "error"); - assert_eq!(parsed["data-dir"], "/tmp/codex"); + assert_eq!(parsed["data-dir"], "/tmp/storage"); assert_eq!(parsed["storage-quota"], 1048576); assert_eq!(parsed["max-peers"], 50); assert!(parsed["listen-addrs"].is_array()); @@ -522,7 +507,7 @@ mod tests { #[test] fn test_json_deserialization_minimal() { let json_str = r#"{"log-level":"info"}"#; - let config = CodexConfig::from_json(json_str).expect("Failed to deserialize from JSON"); + let config = StorageConfig::from_json(json_str).expect("Failed to deserialize from JSON"); // Minimal JSON assert_eq!(config.log_level, Some(LogLevel::Info)); @@ -533,7 +518,7 @@ mod tests { #[test] fn test_json_deserialization_with_empty_vectors() { let json_str = r#"{"log-level":"debug","listen-addrs":[],"bootstrap-node":[]}"#; - let config = CodexConfig::from_json(json_str).expect("Failed to deserialize from JSON"); + let config = StorageConfig::from_json(json_str).expect("Failed to deserialize from JSON"); // JSON with empty vectors assert_eq!(config.log_level, Some(LogLevel::Debug)); @@ -549,7 +534,7 @@ mod tests { "metrics":true, "metrics-address":"192.168.1.100", "metrics-port":9000, - "data-dir":"/tmp/codex", + "data-dir":"/tmp/storage", "listen-addrs":["/ip4/127.0.0.1/tcp/8080"], "nat":"any", "disc-port":8090, @@ -564,10 +549,10 @@ mod tests { "block-mn":500, "block-retries":1000, "cache-size":1048576, - "log-file":"/var/log/codex.log" + "log-file":"/var/log/storage.log" }"#; - let config = CodexConfig::from_json(json_str).expect("Failed to deserialize from JSON"); + let config = StorageConfig::from_json(json_str).expect("Failed to deserialize from JSON"); // Full JSON assert_eq!(config.log_level, Some(LogLevel::Error)); @@ -575,7 +560,7 @@ mod tests { assert_eq!(config.metrics_enabled, Some(true)); assert_eq!(config.metrics_address, Some("192.168.1.100".to_string())); assert_eq!(config.metrics_port, Some(9000)); - assert_eq!(config.data_dir, Some(PathBuf::from("/tmp/codex"))); + assert_eq!(config.data_dir, Some(PathBuf::from("/tmp/storage"))); assert_eq!(config.listen_addrs, vec!["/ip4/127.0.0.1/tcp/8080"]); assert_eq!(config.nat, Some("any".to_string())); assert_eq!(config.discovery_port, Some(8090)); @@ -590,7 +575,7 @@ mod tests { assert_eq!(config.block_maintenance_number_of_blocks, Some(500)); assert_eq!(config.block_retries, Some(1000)); assert_eq!(config.cache_size, Some(1048576)); - assert_eq!(config.log_file, Some(PathBuf::from("/var/log/codex.log"))); + assert_eq!(config.log_file, Some(PathBuf::from("/var/log/storage.log"))); } #[test] @@ -616,7 +601,7 @@ mod tests { #[test] fn test_listen_addrs_builder() { - let config = CodexConfig::new().listen_addrs(vec![ + let config = StorageConfig::new().listen_addrs(vec![ "/ip4/127.0.0.1/tcp/8080".to_string(), "/ip4/0.0.0.0/tcp/8080".to_string(), ]); @@ -628,7 +613,7 @@ mod tests { #[test] fn test_add_listen_addr_builder() { - let config = CodexConfig::new() + let config = StorageConfig::new() .add_listen_addr("/ip4/127.0.0.1/tcp/8080") .add_listen_addr("/ip4/0.0.0.0/tcp/8080"); @@ -639,13 +624,13 @@ mod tests { #[test] fn test_log_format_builder() { - let config = CodexConfig::new().log_format(LogFormat::Json); + let config = StorageConfig::new().log_format(LogFormat::Json); assert_eq!(config.log_format, Some(LogFormat::Json)); } #[test] fn test_metrics_builder() { - let config = CodexConfig::new() + let config = StorageConfig::new() .enable_metrics(true) .metrics_address("192.168.1.100") .metrics_port(9000); @@ -657,13 +642,13 @@ mod tests { #[test] fn test_nat_builder() { - let config = CodexConfig::new().nat("any"); + let config = StorageConfig::new().nat("any"); assert_eq!(config.nat, Some("any".to_string())); } #[test] fn test_net_priv_key_file_builder() { - let config = CodexConfig::new().net_priv_key_file("/path/to/key"); + let config = StorageConfig::new().net_priv_key_file("/path/to/key"); assert_eq!( config.net_priv_key_file, Some(PathBuf::from("/path/to/key")) @@ -672,19 +657,19 @@ mod tests { #[test] fn test_num_threads_builder() { - let config = CodexConfig::new().num_threads(4); + let config = StorageConfig::new().num_threads(4); assert_eq!(config.num_threads, Some(4)); } #[test] fn test_agent_string_builder() { - let config = CodexConfig::new().agent_string("CustomAgent/1.0"); + let config = StorageConfig::new().agent_string("CustomAgent/1.0"); assert_eq!(config.agent_string, Some("CustomAgent/1.0".to_string())); } #[test] fn test_block_config_builders() { - let config = CodexConfig::new() + let config = StorageConfig::new() .block_ttl(86400) // 1 day .block_maintenance_interval(600) // 10 minutes .block_maintenance_number_of_blocks(500) @@ -698,22 +683,22 @@ mod tests { #[test] fn test_cache_size_builder() { - let config = CodexConfig::new().cache_size(1024 * 1024); // 1 MB + let config = StorageConfig::new().cache_size(1024 * 1024); // 1 MB assert_eq!(config.cache_size, Some(1024 * 1024)); } #[test] fn test_log_file_builder() { - let config = CodexConfig::new().log_file("/var/log/codex.log"); - assert_eq!(config.log_file, Some(PathBuf::from("/var/log/codex.log"))); + let config = StorageConfig::new().log_file("/var/log/storage.log"); + assert_eq!(config.log_file, Some(PathBuf::from("/var/log/storage.log"))); } #[test] fn test_comprehensive_builder() { - let config = CodexConfig::new() + let config = StorageConfig::new() .log_level(LogLevel::Debug) .log_format(LogFormat::Json) - .data_dir("/tmp/codex") + .data_dir("/tmp/storage") .listen_addrs(vec!["/ip4/127.0.0.1/tcp/8080".to_string()]) .enable_metrics(true) .metrics_address("127.0.0.1") @@ -729,7 +714,7 @@ mod tests { assert_eq!(config.log_level, Some(LogLevel::Debug)); assert_eq!(config.log_format, Some(LogFormat::Json)); - assert_eq!(config.data_dir, Some(PathBuf::from("/tmp/codex"))); + assert_eq!(config.data_dir, Some(PathBuf::from("/tmp/storage"))); assert_eq!(config.listen_addrs.len(), 1); assert_eq!(config.listen_addrs[0], "/ip4/127.0.0.1/tcp/8080"); assert_eq!(config.metrics_enabled, Some(true)); diff --git a/src/node/lifecycle.rs b/src/node/lifecycle.rs index 30b8bfe..0650c39 100644 --- a/src/node/lifecycle.rs +++ b/src/node/lifecycle.rs @@ -1,40 +1,41 @@ -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; use crate::ffi::{ - codex_close, codex_destroy, codex_new, codex_peer_id, codex_repo, codex_revision, codex_spr, - codex_start, codex_stop, codex_version, free_c_string, string_to_c_string, + free_c_string, storage_close, storage_destroy, storage_new, storage_peer_id, storage_repo, + storage_revision, storage_spr, storage_start, storage_stop, storage_version, + string_to_c_string, }; -use crate::node::config::CodexConfig; +use crate::node::config::StorageConfig; use libc::c_void; use std::ptr; use std::sync::{Arc, Mutex}; #[derive(Clone)] -pub struct CodexNode { - inner: Arc>, +pub struct StorageNode { + inner: Arc>, } -struct CodexNodeInner { +struct StorageNodeInner { ctx: *mut c_void, started: bool, } -unsafe impl Send for CodexNodeInner {} -unsafe impl Sync for CodexNodeInner {} +unsafe impl Send for StorageNodeInner {} +unsafe impl Sync for StorageNodeInner {} -unsafe impl Send for CodexNode {} -unsafe impl Sync for CodexNode {} +unsafe impl Send for StorageNode {} +unsafe impl Sync for StorageNode {} -impl CodexNode { - pub fn new(config: CodexConfig) -> Result { - with_libcodex_lock(|| { +impl StorageNode { + pub fn new(config: StorageConfig) -> Result { + with_libstorage_lock(|| { let json_config = config.to_json()?; let c_json_config = string_to_c_string(&json_config); let future = CallbackFuture::new(); let node_ctx = unsafe { - let node_ctx = codex_new( + let node_ctx = storage_new( c_json_config, Some(c_callback), future.context_ptr() as *mut c_void, @@ -43,7 +44,7 @@ impl CodexNode { free_c_string(c_json_config); if node_ctx.is_null() { - return Err(CodexError::node_error("new", "Failed to create node")); + return Err(StorageError::node_error("new", "Failed to create node")); } node_ctx @@ -51,8 +52,8 @@ impl CodexNode { let _result = future.wait()?; - Ok(CodexNode { - inner: Arc::new(Mutex::new(CodexNodeInner { + Ok(StorageNode { + inner: Arc::new(Mutex::new(StorageNodeInner { ctx: node_ctx, started: false, })), @@ -63,13 +64,13 @@ impl CodexNode { pub fn start(&mut self) -> Result<()> { let mut inner = self.inner.lock().unwrap(); if inner.started { - return Err(CodexError::node_error("start", "Node is already started")); + return Err(StorageError::node_error("start", "Node is already started")); } let future = CallbackFuture::new(); let result = unsafe { - codex_start( + storage_start( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -77,7 +78,7 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("start", "Failed to start node")); + return Err(StorageError::node_error("start", "Failed to start node")); } let _result = future.wait()?; @@ -93,7 +94,7 @@ impl CodexNode { { let inner = node.inner.lock().unwrap(); if inner.started { - return Err(CodexError::node_error( + return Err(StorageError::node_error( "start_async_send", "Node is already started", )); @@ -107,11 +108,12 @@ impl CodexNode { inner.ctx as *mut _ }; - let result = - unsafe { codex_start(ctx, Some(c_callback), future.context_ptr() as *mut c_void) }; + let result = unsafe { + storage_start(ctx, Some(c_callback), future.context_ptr() as *mut c_void) + }; if result != 0 { - return Err(CodexError::node_error( + return Err(StorageError::node_error( "start_async_send", "Failed to start node", )); @@ -132,13 +134,13 @@ impl CodexNode { pub fn stop(&mut self) -> Result<()> { let mut inner = self.inner.lock().unwrap(); if !inner.started { - return Err(CodexError::node_error("stop", "Node is not started")); + return Err(StorageError::node_error("stop", "Node is not started")); } let future = CallbackFuture::new(); let result = unsafe { - codex_stop( + storage_stop( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -146,7 +148,7 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("stop", "Failed to stop node")); + return Err(StorageError::node_error("stop", "Failed to stop node")); } inner.started = false; @@ -160,7 +162,7 @@ impl CodexNode { { let inner = node.inner.lock().unwrap(); if !inner.started { - return Err(CodexError::node_error( + return Err(StorageError::node_error( "stop_async_send", "Node is not started", )); @@ -175,10 +177,10 @@ impl CodexNode { }; let result = - unsafe { codex_stop(ctx, Some(c_callback), future.context_ptr() as *mut c_void) }; + unsafe { storage_stop(ctx, Some(c_callback), future.context_ptr() as *mut c_void) }; if result != 0 { - return Err(CodexError::node_error( + return Err(StorageError::node_error( "stop_async_send", "Failed to stop node", )); @@ -198,7 +200,7 @@ impl CodexNode { pub fn destroy(self) -> Result<()> { if Arc::strong_count(&self.inner) != 1 { - return Err(CodexError::node_error( + return Err(StorageError::node_error( "destroy", "Cannot destroy: multiple references exist", )); @@ -206,13 +208,13 @@ impl CodexNode { let mut inner = self.inner.lock().unwrap(); if inner.started { - return Err(CodexError::node_error("destroy", "Node is still started")); + return Err(StorageError::node_error("destroy", "Node is still started")); } let future = CallbackFuture::new(); let result = unsafe { - codex_close( + storage_close( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -220,12 +222,12 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("destroy", "Failed to close node")); + return Err(StorageError::node_error("destroy", "Failed to close node")); } future.wait()?; - unsafe { codex_destroy(inner.ctx as *mut _, None, ptr::null_mut()) }; + unsafe { storage_destroy(inner.ctx as *mut _, None, ptr::null_mut()) }; inner.ctx = ptr::null_mut(); Ok(()) @@ -237,7 +239,7 @@ impl CodexNode { let future = CallbackFuture::new(); let result = unsafe { - codex_version( + storage_version( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -245,7 +247,7 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("version", "Failed to get version")); + return Err(StorageError::node_error("version", "Failed to get version")); } let version = future.wait()?; @@ -259,7 +261,7 @@ impl CodexNode { let future = CallbackFuture::new(); let result = unsafe { - codex_revision( + storage_revision( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -267,7 +269,10 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("revision", "Failed to get revision")); + return Err(StorageError::node_error( + "revision", + "Failed to get revision", + )); } let revision = future.wait()?; @@ -281,7 +286,7 @@ impl CodexNode { let future = CallbackFuture::new(); let result = unsafe { - codex_repo( + storage_repo( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -289,7 +294,7 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("repo", "Failed to get repo path")); + return Err(StorageError::node_error("repo", "Failed to get repo path")); } let repo = future.wait()?; @@ -303,7 +308,7 @@ impl CodexNode { let future = CallbackFuture::new(); let result = unsafe { - codex_spr( + storage_spr( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -311,7 +316,7 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("spr", "Failed to get SPR")); + return Err(StorageError::node_error("spr", "Failed to get SPR")); } let spr = future.wait()?; @@ -325,7 +330,7 @@ impl CodexNode { let future = CallbackFuture::new(); let result = unsafe { - codex_peer_id( + storage_peer_id( inner.ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -333,7 +338,7 @@ impl CodexNode { }; if result != 0 { - return Err(CodexError::node_error("peer_id", "Failed to get peer ID")); + return Err(StorageError::node_error("peer_id", "Failed to get peer ID")); } let peer_id = future.wait()?; @@ -364,28 +369,28 @@ impl CodexNode { where F: FnOnce(*mut c_void) -> R, { - with_libcodex_lock(|| { + with_libstorage_lock(|| { let inner = self.inner.lock().unwrap(); f(inner.ctx) }) } } -impl Drop for CodexNode { +impl Drop for StorageNode { fn drop(&mut self) { if Arc::strong_count(&self.inner) == 1 { let mut inner = self.inner.lock().unwrap(); if !inner.ctx.is_null() && inner.started { - let _ = unsafe { - codex_stop(inner.ctx as *mut _, None, ptr::null_mut()); - }; + unsafe { + storage_stop(inner.ctx as *mut _, None, ptr::null_mut()); + } inner.started = false; } if !inner.ctx.is_null() { - let _ = unsafe { - codex_destroy(inner.ctx as *mut _, None, ptr::null_mut()); - }; + unsafe { + storage_destroy(inner.ctx as *mut _, None, ptr::null_mut()); + } inner.ctx = ptr::null_mut(); } } diff --git a/src/node/mod.rs b/src/node/mod.rs index db6be80..fdcace5 100644 --- a/src/node/mod.rs +++ b/src/node/mod.rs @@ -1,10 +1,10 @@ -//! Node management for Codex +//! Node management for Storage //! //! This module provides functionality for creating, configuring, starting, -//! stopping, and destroying Codex nodes. +//! stopping, and destroying Storage nodes. pub mod config; pub mod lifecycle; -pub use config::{CodexConfig, LogFormat, LogLevel, RepoKind}; -pub use lifecycle::CodexNode; +pub use config::{LogFormat, LogLevel, RepoKind, StorageConfig}; +pub use lifecycle::StorageNode; diff --git a/src/p2p/connection.rs b/src/p2p/connection.rs index 9a70d95..375e795 100644 --- a/src/p2p/connection.rs +++ b/src/p2p/connection.rs @@ -1,24 +1,24 @@ -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_connect, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_connect, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use libc::{c_char, c_void}; -pub async fn connect(node: &CodexNode, peer_id: &str, peer_addresses: &[String]) -> Result<()> { +pub async fn connect(node: &StorageNode, peer_id: &str, peer_addresses: &[String]) -> Result<()> { let node = node.clone(); let peer_id = peer_id.to_string(); let peer_addresses = peer_addresses.to_vec(); tokio::task::spawn_blocking(move || { if peer_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_id", "Peer ID cannot be empty", )); } if peer_addresses.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_addresses", "At least one peer address must be provided", )); @@ -33,12 +33,12 @@ pub async fn connect(node: &CodexNode, peer_id: &str, peer_addresses: &[String]) .map(|addr| string_to_c_string(addr)) .collect(); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_connect( + storage_connect( ctx as *mut _, c_peer_id, - c_addresses.as_ptr() as *mut *mut c_char, + c_addresses.as_ptr() as *mut *const c_char, c_addresses.len(), Some(c_callback), future.context_ptr() as *mut c_void, @@ -54,7 +54,7 @@ pub async fn connect(node: &CodexNode, peer_id: &str, peer_addresses: &[String]) } if result != 0 { - return Err(CodexError::p2p_error("Failed to connect to peer")); + return Err(StorageError::p2p_error("Failed to connect to peer")); } future.wait()?; @@ -65,7 +65,7 @@ pub async fn connect(node: &CodexNode, peer_id: &str, peer_addresses: &[String]) } pub async fn connect_to_multiple( - node: &CodexNode, + node: &StorageNode, peer_connections: Vec<(String, Vec)>, ) -> Vec> { let mut results = Vec::with_capacity(peer_connections.len()); @@ -80,34 +80,34 @@ pub async fn connect_to_multiple( pub fn validate_peer_id(peer_id: &str) -> Result<()> { if peer_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_id", "Peer ID cannot be empty", )); } if peer_id.len() < 10 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_id", "Peer ID is too short", )); } if peer_id.len() > 100 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_id", "Peer ID is too long", )); } - let valid_prefixes = vec!["12D3KooW", "Qm", "bafy", "bafk"]; + let valid_prefixes = ["12D3KooW", "Qm", "bafy", "bafk"]; let has_valid_prefix = valid_prefixes .iter() .any(|&prefix| peer_id.starts_with(prefix)); if !has_valid_prefix { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_id", "Peer ID has invalid format or prefix", )); @@ -118,7 +118,7 @@ pub fn validate_peer_id(peer_id: &str) -> Result<()> { pub fn validate_addresses(addresses: &[String]) -> Result<()> { if addresses.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "addresses", "At least one address must be provided", )); @@ -126,20 +126,20 @@ pub fn validate_addresses(addresses: &[String]) -> Result<()> { for (i, address) in addresses.iter().enumerate() { if address.is_empty() { - return Err(CodexError::invalid_parameter( - &format!("addresses[{}]", i), + return Err(StorageError::invalid_parameter( + format!("addresses[{}]", i), "Address cannot be empty", )); } if !address.starts_with('/') { - return Err(CodexError::invalid_parameter( - &format!("addresses[{}]", i), + return Err(StorageError::invalid_parameter( + format!("addresses[{}]", i), "Address must start with '/'", )); } - let valid_protocols = vec![ + let valid_protocols = [ "/ip4", "/ip6", "/dns4", "/dns6", "/dnsaddr", "/tcp", "/udp", "/quic", "/ws", "/wss", "/p2p", "/ipfs", ]; @@ -149,8 +149,8 @@ pub fn validate_addresses(addresses: &[String]) -> Result<()> { .any(|&protocol| address.contains(protocol)); if !has_valid_protocol { - return Err(CodexError::invalid_parameter( - &format!("addresses[{}]", i), + return Err(StorageError::invalid_parameter( + format!("addresses[{}]", i), "Address contains invalid protocol", )); } diff --git a/src/p2p/discovery.rs b/src/p2p/discovery.rs index 2567ca7..b5e1bb1 100644 --- a/src/p2p/discovery.rs +++ b/src/p2p/discovery.rs @@ -1,17 +1,17 @@ -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_peer_debug, codex_peer_id, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_peer_debug, storage_peer_id, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use crate::p2p::types::PeerRecord; use libc::c_void; -pub async fn get_peer_info(node: &CodexNode, peer_id: &str) -> Result { +pub async fn get_peer_info(node: &StorageNode, peer_id: &str) -> Result { let node = node.clone(); let peer_id = peer_id.to_string(); tokio::task::spawn_blocking(move || { if peer_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "peer_id", "Peer ID cannot be empty", )); @@ -19,12 +19,12 @@ pub async fn get_peer_info(node: &CodexNode, peer_id: &str) -> Result Result Result Result { +pub async fn get_peer_id(node: &StorageNode) -> Result { let node = node.clone(); tokio::task::spawn_blocking(move || { let future = CallbackFuture::new(); - with_libcodex_lock(|| { + with_libstorage_lock(|| { let result = unsafe { node.with_ctx(|ctx| { - codex_peer_id( + storage_peer_id( ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -72,7 +73,7 @@ pub async fn get_peer_id(node: &CodexNode) -> Result { }; if result != 0 { - return Err(CodexError::p2p_error("Failed to get peer ID")); + return Err(StorageError::p2p_error("Failed to get peer ID")); } Ok(()) diff --git a/src/p2p/mod.rs b/src/p2p/mod.rs index efcf5ed..98f5c5f 100644 --- a/src/p2p/mod.rs +++ b/src/p2p/mod.rs @@ -1,6 +1,6 @@ -//! P2P operations for Codex +//! P2P operations for Storage //! -//! This module provides functionality for connecting to peers in the Codex network. +//! This module provides functionality for connecting to peers in the Storage network. pub mod connection; pub mod discovery; diff --git a/src/p2p/types.rs b/src/p2p/types.rs index 361cc13..9a9c5b1 100644 --- a/src/p2p/types.rs +++ b/src/p2p/types.rs @@ -65,12 +65,12 @@ impl PeerInfo { /// Check if the connection is inbound pub fn is_inbound(&self) -> bool { - self.direction.as_ref().map_or(false, |d| d == "inbound") + self.direction.as_ref().is_some_and(|d| d == "inbound") } /// Check if the connection is outbound pub fn is_outbound(&self) -> bool { - self.direction.as_ref().map_or(false, |d| d == "outbound") + self.direction.as_ref().is_some_and(|d| d == "outbound") } /// Get a human-readable latency string @@ -278,12 +278,12 @@ impl PeerRecord { /// Check if the connection is inbound pub fn is_inbound(&self) -> bool { - self.direction.as_ref().map_or(false, |d| d == "inbound") + self.direction.as_ref().is_some_and(|d| d == "inbound") } /// Check if the connection is outbound pub fn is_outbound(&self) -> bool { - self.direction.as_ref().map_or(false, |d| d == "outbound") + self.direction.as_ref().is_some_and(|d| d == "outbound") } } @@ -365,8 +365,8 @@ mod tests { .connected(true) .direction("outbound".to_string()) .latency(50) - .protocols(vec!["/codex/1.0.0".to_string()]) - .user_agent("codex-rust-bindings/0.1.0".to_string()) + .protocols(vec!["/storage/1.0.0".to_string()]) + .user_agent("storage-rust-bindings/0.1.0".to_string()) .last_seen("2023-01-01T12:00:00Z".to_string()) .connection_duration(1800) .bytes_sent(1024 * 1024) @@ -378,7 +378,7 @@ mod tests { assert!(peer_record.connected); assert_eq!(peer_record.latency_ms, Some(50)); assert_eq!(peer_record.protocols.len(), 1); - assert!(peer_record.supports_protocol("/codex/1.0.0")); + assert!(peer_record.supports_protocol("/storage/1.0.0")); assert_eq!(peer_record.total_bytes(), 3 * 1024 * 1024); assert_eq!(peer_record.duration_string(), "30m 0s"); assert_eq!(peer_record.bytes_string(), "3.0MB"); diff --git a/src/storage/crud.rs b/src/storage/crud.rs index 2d2daa7..25a3f9c 100644 --- a/src/storage/crud.rs +++ b/src/storage/crud.rs @@ -1,28 +1,30 @@ -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; use crate::ffi::{ - codex_storage_delete, codex_storage_exists, codex_storage_fetch, free_c_string, - string_to_c_string, + free_c_string, storage_delete, storage_exists, storage_fetch, string_to_c_string, }; -use crate::node::lifecycle::CodexNode; +use crate::node::lifecycle::StorageNode; use libc::c_void; -pub async fn fetch(node: &CodexNode, cid: &str) -> Result { +pub async fn fetch(node: &StorageNode, cid: &str) -> Result { let node = node.clone(); let cid = cid.to_string(); tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } let future = CallbackFuture::new(); let c_cid = string_to_c_string(&cid); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_storage_fetch( + storage_fetch( ctx as *mut _, c_cid, Some(c_callback), @@ -36,7 +38,7 @@ pub async fn fetch(node: &CodexNode, cid: &str) -> Result Result Result<()> { +pub async fn delete(node: &StorageNode, cid: &str) -> Result<()> { let node = node.clone(); let cid = cid.to_string(); tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } let future = CallbackFuture::new(); let c_cid = string_to_c_string(&cid); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_storage_delete( + storage_delete( ctx as *mut _, c_cid, Some(c_callback), @@ -81,7 +86,7 @@ pub async fn delete(node: &CodexNode, cid: &str) -> Result<()> { } if result != 0 { - return Err(CodexError::storage_error( + return Err(StorageError::storage_operation_error( "delete", "Failed to delete content", )); @@ -94,22 +99,25 @@ pub async fn delete(node: &CodexNode, cid: &str) -> Result<()> { .await? } -pub async fn exists(node: &CodexNode, cid: &str) -> Result { +pub async fn exists(node: &StorageNode, cid: &str) -> Result { let node = node.clone(); let cid = cid.to_string(); tokio::task::spawn_blocking(move || { if cid.is_empty() { - return Err(CodexError::invalid_parameter("cid", "CID cannot be empty")); + return Err(StorageError::invalid_parameter( + "cid", + "CID cannot be empty", + )); } let future = CallbackFuture::new(); let c_cid = string_to_c_string(&cid); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_storage_exists( + storage_exists( ctx as *mut _, c_cid, Some(c_callback), @@ -123,7 +131,7 @@ pub async fn exists(node: &CodexNode, cid: &str) -> Result { } if result != 0 { - return Err(CodexError::storage_error( + return Err(StorageError::storage_operation_error( "exists", "Failed to check if content exists", )); @@ -132,7 +140,7 @@ pub async fn exists(node: &CodexNode, cid: &str) -> Result { let exists_str = future.wait()?; let exists = exists_str.parse::().map_err(|e| { - CodexError::library_error(format!("Failed to parse exists result: {}", e)) + StorageError::library_error(format!("Failed to parse exists result: {}", e)) })?; Ok(exists) diff --git a/src/storage/mod.rs b/src/storage/mod.rs index 6e467d8..ac70b0b 100644 --- a/src/storage/mod.rs +++ b/src/storage/mod.rs @@ -1,4 +1,4 @@ -//! Storage management operations for Codex +//! Storage management operations for Storage //! //! This module provides core storage functionality that directly maps to the C API. //! It includes operations for listing manifests, managing storage space, and basic diff --git a/src/storage/space.rs b/src/storage/space.rs index 1498a95..c883443 100644 --- a/src/storage/space.rs +++ b/src/storage/space.rs @@ -1,7 +1,7 @@ -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_storage_list, codex_storage_space}; -use crate::node::lifecycle::CodexNode; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; +use crate::ffi::{storage_list, storage_space}; +use crate::node::lifecycle::StorageNode; use libc::c_void; use serde::{Deserialize, Serialize}; @@ -41,15 +41,15 @@ pub struct Space { pub quota_reserved_bytes: u64, } -pub async fn manifests(node: &CodexNode) -> Result> { +pub async fn manifests(node: &StorageNode) -> Result> { let node = node.clone(); tokio::task::spawn_blocking(move || { let future = CallbackFuture::new(); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_storage_list( + storage_list( ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -58,7 +58,7 @@ pub async fn manifests(node: &CodexNode) -> Result> { }); if result != 0 { - return Err(CodexError::storage_error( + return Err(StorageError::storage_operation_error( "manifests", "Failed to list manifests", )); @@ -67,7 +67,9 @@ pub async fn manifests(node: &CodexNode) -> Result> { let manifests_json = future.wait()?; let manifests_with_cid: Vec = serde_json::from_str(&manifests_json) - .map_err(|e| CodexError::library_error(format!("Failed to parse manifests: {}", e)))?; + .map_err(|e| { + StorageError::library_error(format!("Failed to parse manifests: {}", e)) + })?; let manifests: Vec = manifests_with_cid .into_iter() @@ -83,15 +85,15 @@ pub async fn manifests(node: &CodexNode) -> Result> { .await? } -pub async fn space(node: &CodexNode) -> Result { +pub async fn space(node: &StorageNode) -> Result { let node = node.clone(); tokio::task::spawn_blocking(move || { let future = CallbackFuture::new(); - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { - codex_storage_space( + storage_space( ctx as *mut _, Some(c_callback), future.context_ptr() as *mut c_void, @@ -100,7 +102,7 @@ pub async fn space(node: &CodexNode) -> Result { }); if result != 0 { - return Err(CodexError::storage_error( + return Err(StorageError::storage_operation_error( "space", "Failed to get storage space", )); @@ -108,8 +110,9 @@ pub async fn space(node: &CodexNode) -> Result { let space_json = future.wait()?; - let space: Space = serde_json::from_str(&space_json) - .map_err(|e| CodexError::library_error(format!("Failed to parse space info: {}", e)))?; + let space: Space = serde_json::from_str(&space_json).map_err(|e| { + StorageError::library_error(format!("Failed to parse space info: {}", e)) + })?; Ok(space) }) diff --git a/src/storage/types.rs b/src/storage/types.rs index 2a68f07..af6b8e1 100644 --- a/src/storage/types.rs +++ b/src/storage/types.rs @@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize}; /// Manifest information for a stored content -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Manifest { /// Content ID (CID) - set separately in fetch() #[serde(skip)] @@ -83,7 +83,7 @@ impl Manifest { if self.block_size == 0 { 0 } else { - (self.dataset_size + self.block_size - 1) / self.block_size + self.dataset_size.div_ceil(self.block_size) } } @@ -100,11 +100,9 @@ impl Manifest { /// Get the file extension if this is a file pub fn file_extension(&self) -> Option { if self.is_file() { - if let Some(dot_pos) = self.filename.rfind('.') { - Some(self.filename[dot_pos + 1..].to_lowercase()) - } else { - None - } + self.filename + .rfind('.') + .map(|dot_pos| self.filename[dot_pos + 1..].to_lowercase()) } else { None } @@ -116,22 +114,8 @@ impl Manifest { } } -impl Default for Manifest { - fn default() -> Self { - Self { - cid: String::new(), - tree_cid: String::new(), - dataset_size: 0, - block_size: 0, - filename: String::new(), - mimetype: String::new(), - protected: false, - } - } -} - /// Storage space information -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct Space { /// Total number of blocks stored by the node #[serde(rename = "totalBlocks")] @@ -225,14 +209,3 @@ impl Space { bytesize::ByteSize::b(self.available_bytes()).to_string() } } - -impl Default for Space { - fn default() -> Self { - Self { - total_blocks: 0, - quota_max_bytes: 0, - quota_used_bytes: 0, - quota_reserved_bytes: 0, - } - } -} diff --git a/src/upload/chunks.rs b/src/upload/chunks.rs index 3699247..f577db9 100644 --- a/src/upload/chunks.rs +++ b/src/upload/chunks.rs @@ -1,23 +1,23 @@ -//! Chunk upload operations for Codex +//! Chunk upload operations for Storage //! //! This module provides functionality for uploading individual chunks of data //! as part of an upload session. Chunks are the basic unit of data transfer -//! in the Codex network. +//! in the Storage network. -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_upload_chunk, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_upload_chunk, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use libc::c_void; /// Upload a chunk of data as part of an ongoing upload session /// -/// Uploads a single chunk of data to the Codex network. The chunk will be +/// Uploads a single chunk of data to the Storage network. The chunk will be /// associated with the specified session ID. /// /// # Arguments /// -/// * `node` - The Codex node to use for the upload +/// * `node` - The Storage node to use for the upload /// * `session_id` - The session ID returned by `upload_init` /// * `chunk` - The chunk data to upload /// @@ -31,20 +31,20 @@ use libc::c_void; /// - The session ID is empty /// - The chunk is empty /// - The upload fails for any reason -pub async fn upload_chunk(node: &CodexNode, session_id: &str, chunk: Vec) -> Result<()> { +pub async fn upload_chunk(node: &StorageNode, session_id: &str, chunk: Vec) -> Result<()> { let node = node.clone(); let session_id = session_id.to_string(); tokio::task::spawn_blocking(move || { if session_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "session_id", "Session ID cannot be empty", )); } if chunk.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "chunk", "Chunk cannot be empty", )); @@ -56,10 +56,10 @@ pub async fn upload_chunk(node: &CodexNode, session_id: &str, chunk: Vec) -> let chunk_len = chunk.len(); let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_session_id = string_to_c_string(&session_id); - let result = codex_upload_chunk( + let result = storage_upload_chunk( ctx as *mut _, c_session_id, chunk_ptr, @@ -75,7 +75,7 @@ pub async fn upload_chunk(node: &CodexNode, session_id: &str, chunk: Vec) -> }); if result != 0 { - return Err(CodexError::upload_error("Failed to upload chunk")); + return Err(StorageError::upload_error("Failed to upload chunk")); } future.wait()?; @@ -92,7 +92,7 @@ pub async fn upload_chunk(node: &CodexNode, session_id: &str, chunk: Vec) -> /// /// # Arguments /// -/// * `node` - The Codex node to use for the upload +/// * `node` - The Storage node to use for the upload /// * `session_id` - The session ID returned by `upload_init` /// * `chunks` - A vector of chunks to upload /// @@ -103,10 +103,14 @@ pub async fn upload_chunk(node: &CodexNode, session_id: &str, chunk: Vec) -> /// # Errors /// /// Returns an error if any chunk fails to upload -pub async fn upload_chunks(node: &CodexNode, session_id: &str, chunks: Vec>) -> Result<()> { +pub async fn upload_chunks( + node: &StorageNode, + session_id: &str, + chunks: Vec>, +) -> Result<()> { for (index, chunk) in chunks.into_iter().enumerate() { upload_chunk(node, session_id, chunk).await.map_err(|e| { - CodexError::upload_error(format!("Failed to upload chunk {}: {}", index, e)) + StorageError::upload_error(format!("Failed to upload chunk {}: {}", index, e)) })?; } Ok(()) diff --git a/src/upload/file.rs b/src/upload/file.rs index 2354825..2c13440 100644 --- a/src/upload/file.rs +++ b/src/upload/file.rs @@ -1,13 +1,13 @@ -//! High-level file upload operations for Codex +//! High-level file upload operations for Storage //! //! This module provides convenient high-level functions for uploading files -//! and readers to the Codex network. These functions handle the complete +//! and readers to the Storage network. These functions handle the complete //! upload lifecycle including session management and chunking. use crate::callback::{c_callback, CallbackFuture}; -use crate::error::{CodexError, Result}; -use crate::ffi::{codex_upload_file, free_c_string, string_to_c_string}; -use crate::node::lifecycle::CodexNode; +use crate::error::{Result, StorageError}; +use crate::ffi::{free_c_string, storage_upload_file, string_to_c_string}; +use crate::node::lifecycle::StorageNode; use crate::upload::types::{UploadOptions, UploadProgress, UploadResult}; use libc::c_void; use std::io::Read; @@ -15,13 +15,13 @@ use std::path::Path; /// Upload a file from the filesystem /// -/// High-level function that uploads a file from the filesystem to the Codex network. +/// High-level function that uploads a file from the filesystem to the Storage network. /// This function handles the complete upload process including file validation, /// session creation, and progress tracking. /// /// # Arguments /// -/// * `node` - The Codex node to use for the upload +/// * `node` - The Storage node to use for the upload /// * `options` - Upload options including file path and configuration /// /// # Returns @@ -34,13 +34,13 @@ use std::path::Path; /// - No file path is specified in options /// - The file doesn't exist /// - The upload fails for any reason -pub async fn upload_file(node: &CodexNode, options: UploadOptions) -> Result { +pub async fn upload_file(node: &StorageNode, options: UploadOptions) -> Result { let node = node.clone(); let options = options.clone(); tokio::task::spawn_blocking(move || { if options.filepath.is_none() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "filepath", "File path must be specified for file upload", )); @@ -49,7 +49,7 @@ pub async fn upload_file(node: &CodexNode, options: UploadOptions) -> Result Result Result Result Result( - node: &CodexNode, + node: &StorageNode, options: UploadOptions, reader: R, ) -> Result @@ -158,7 +158,7 @@ where } Err(e) => { let _ = upload_cancel_sync(&node, &session_id); - return Err(CodexError::from(e)); + return Err(StorageError::from(e)); } } } @@ -176,7 +176,7 @@ where } /// Synchronous version of upload_init for internal use -fn upload_init_sync(node: &CodexNode, options: &UploadOptions) -> Result { +fn upload_init_sync(node: &StorageNode, options: &UploadOptions) -> Result { options.validate()?; let future = CallbackFuture::new(); @@ -190,10 +190,10 @@ fn upload_init_sync(node: &CodexNode, options: &UploadOptions) -> Result let chunk_size = options.chunk_size.unwrap_or(1024 * 1024); let context_ptr = future.context_ptr() as *mut c_void; - let result = crate::callback::with_libcodex_lock(|| unsafe { + let result = crate::callback::with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_filepath = crate::ffi::string_to_c_string(filepath_str); - let result = crate::ffi::codex_upload_init( + let result = crate::ffi::storage_upload_init( ctx as *mut _, c_filepath, chunk_size, @@ -210,7 +210,7 @@ fn upload_init_sync(node: &CodexNode, options: &UploadOptions) -> Result }); if result != 0 { - return Err(CodexError::upload_error("Failed to initialize upload")); + return Err(StorageError::upload_error("Failed to initialize upload")); } let session_id = future.wait()?; @@ -218,16 +218,16 @@ fn upload_init_sync(node: &CodexNode, options: &UploadOptions) -> Result } /// Synchronous version of upload_chunk for internal use -fn upload_chunk_sync(node: &CodexNode, session_id: &str, chunk: &[u8]) -> Result<()> { +fn upload_chunk_sync(node: &StorageNode, session_id: &str, chunk: &[u8]) -> Result<()> { if session_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "session_id", "Session ID cannot be empty", )); } if chunk.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "chunk", "Chunk cannot be empty", )); @@ -239,10 +239,10 @@ fn upload_chunk_sync(node: &CodexNode, session_id: &str, chunk: &[u8]) -> Result let chunk_len = chunk.len(); let context_ptr = future.context_ptr() as *mut c_void; - let result = crate::callback::with_libcodex_lock(|| unsafe { + let result = crate::callback::with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_session_id = crate::ffi::string_to_c_string(session_id); - let result = crate::ffi::codex_upload_chunk( + let result = crate::ffi::storage_upload_chunk( ctx as *mut _, c_session_id, chunk_ptr, @@ -258,7 +258,7 @@ fn upload_chunk_sync(node: &CodexNode, session_id: &str, chunk: &[u8]) -> Result }); if result != 0 { - return Err(CodexError::upload_error("Failed to upload chunk")); + return Err(StorageError::upload_error("Failed to upload chunk")); } future.wait()?; @@ -266,9 +266,9 @@ fn upload_chunk_sync(node: &CodexNode, session_id: &str, chunk: &[u8]) -> Result } /// Synchronous version of upload_finalize for internal use -fn upload_finalize_sync(node: &CodexNode, session_id: &str) -> Result { +fn upload_finalize_sync(node: &StorageNode, session_id: &str) -> Result { if session_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "session_id", "Session ID cannot be empty", )); @@ -278,10 +278,10 @@ fn upload_finalize_sync(node: &CodexNode, session_id: &str) -> Result { let context_ptr = future.context_ptr() as *mut c_void; - let result = crate::callback::with_libcodex_lock(|| unsafe { + let result = crate::callback::with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_session_id = crate::ffi::string_to_c_string(session_id); - let result = crate::ffi::codex_upload_finalize( + let result = crate::ffi::storage_upload_finalize( ctx as *mut _, c_session_id, Some(crate::callback::c_callback), @@ -295,7 +295,7 @@ fn upload_finalize_sync(node: &CodexNode, session_id: &str) -> Result { }); if result != 0 { - return Err(CodexError::upload_error("Failed to finalize upload")); + return Err(StorageError::upload_error("Failed to finalize upload")); } let cid = future.wait()?; @@ -303,9 +303,9 @@ fn upload_finalize_sync(node: &CodexNode, session_id: &str) -> Result { } /// Synchronous version of upload_cancel for internal use -fn upload_cancel_sync(node: &CodexNode, session_id: &str) -> Result<()> { +fn upload_cancel_sync(node: &StorageNode, session_id: &str) -> Result<()> { if session_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "session_id", "Session ID cannot be empty", )); @@ -315,10 +315,10 @@ fn upload_cancel_sync(node: &CodexNode, session_id: &str) -> Result<()> { let context_ptr = future.context_ptr() as *mut c_void; - let result = crate::callback::with_libcodex_lock(|| unsafe { + let result = crate::callback::with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_session_id = crate::ffi::string_to_c_string(session_id); - let result = crate::ffi::codex_upload_cancel( + let result = crate::ffi::storage_upload_cancel( ctx as *mut _, c_session_id, Some(crate::callback::c_callback), @@ -332,7 +332,7 @@ fn upload_cancel_sync(node: &CodexNode, session_id: &str) -> Result<()> { }); if result != 0 { - return Err(CodexError::upload_error("Failed to cancel upload")); + return Err(StorageError::upload_error("Failed to cancel upload")); } future.wait()?; diff --git a/src/upload/mod.rs b/src/upload/mod.rs index 4e1b74c..96a2002 100644 --- a/src/upload/mod.rs +++ b/src/upload/mod.rs @@ -1,6 +1,6 @@ -//! Upload operations for Codex +//! Upload operations for Storage //! -//! This module provides comprehensive upload functionality for the Codex distributed storage network. +//! This module provides comprehensive upload functionality for the Storage distributed storage network. //! It supports both high-level and low-level upload operations, streaming uploads, and progress tracking. //! //! ## High-Level Operations diff --git a/src/upload/session.rs b/src/upload/session.rs index 6b67a38..cd4fa68 100644 --- a/src/upload/session.rs +++ b/src/upload/session.rs @@ -1,16 +1,16 @@ -//! Upload session management for Codex +//! Upload session management for Storage //! //! This module provides low-level session management operations for uploads. //! These functions handle the lifecycle of upload sessions including initialization, //! finalization, and cancellation. -use crate::callback::{c_callback, with_libcodex_lock, CallbackFuture}; -use crate::error::{CodexError, Result}; +use crate::callback::{c_callback, with_libstorage_lock, CallbackFuture}; +use crate::error::{Result, StorageError}; use crate::ffi::{ - codex_upload_cancel, codex_upload_finalize, codex_upload_init, free_c_string, + free_c_string, storage_upload_cancel, storage_upload_finalize, storage_upload_init, string_to_c_string, }; -use crate::node::lifecycle::CodexNode; +use crate::node::lifecycle::StorageNode; use crate::upload::types::UploadOptions; use libc::c_void; @@ -21,13 +21,13 @@ use libc::c_void; /// /// # Arguments /// -/// * `node` - The Codex node to use for the upload +/// * `node` - The Storage node to use for the upload /// * `options` - Upload configuration options /// /// # Returns /// /// A session ID string that identifies this upload session -pub async fn upload_init(node: &CodexNode, options: &UploadOptions) -> Result { +pub async fn upload_init(node: &StorageNode, options: &UploadOptions) -> Result { let node = node.clone(); let options = options.clone(); @@ -45,10 +45,10 @@ pub async fn upload_init(node: &CodexNode, options: &UploadOptions) -> Result Result Result Result { +pub async fn upload_finalize(node: &StorageNode, session_id: &str) -> Result { let node = node.clone(); let session_id = session_id.to_string(); tokio::task::spawn_blocking(move || { if session_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "session_id", "Session ID cannot be empty", )); @@ -103,10 +103,10 @@ pub async fn upload_finalize(node: &CodexNode, session_id: &str) -> Result Result Result Result<()> { +pub async fn upload_cancel(node: &StorageNode, session_id: &str) -> Result<()> { let node = node.clone(); let session_id = session_id.to_string(); tokio::task::spawn_blocking(move || { if session_id.is_empty() { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "session_id", "Session ID cannot be empty", )); @@ -154,11 +154,15 @@ pub async fn upload_cancel(node: &CodexNode, session_id: &str) -> Result<()> { let context_ptr = future.context_ptr() as *mut c_void; - let result = with_libcodex_lock(|| unsafe { + let result = with_libstorage_lock(|| unsafe { node.with_ctx(|ctx| { let c_session_id = string_to_c_string(&session_id); - let result = - codex_upload_cancel(ctx as *mut _, c_session_id, Some(c_callback), context_ptr); + let result = storage_upload_cancel( + ctx as *mut _, + c_session_id, + Some(c_callback), + context_ptr, + ); free_c_string(c_session_id); @@ -167,7 +171,7 @@ pub async fn upload_cancel(node: &CodexNode, session_id: &str) -> Result<()> { }); if result != 0 { - return Err(CodexError::upload_error("Failed to cancel upload")); + return Err(StorageError::upload_error("Failed to cancel upload")); } future.wait()?; diff --git a/src/upload/streaming.rs b/src/upload/streaming.rs index 2c7c5ab..8d2d574 100644 --- a/src/upload/streaming.rs +++ b/src/upload/streaming.rs @@ -213,10 +213,7 @@ where { // Adjust chunk size based on total size if provided let adjusted_options = if let Some(size) = total_size { - let optimal_chunk_size = std::cmp::min( - std::cmp::max(size / 100, 64 * 1024), // At least 64KB, at most 1% of total - 4 * 1024 * 1024, // But never more than 4MB - ); + let optimal_chunk_size = (size / 100).clamp(64 * 1024, 4 * 1024 * 1024); let mut opts = options; opts.chunk_size = Some(optimal_chunk_size); diff --git a/src/upload/types.rs b/src/upload/types.rs index 444fd9e..264f886 100644 --- a/src/upload/types.rs +++ b/src/upload/types.rs @@ -1,22 +1,17 @@ -use crate::error::{CodexError, Result}; +use crate::error::{Result, StorageError}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; use std::sync::Arc; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "lowercase")] pub enum UploadStrategy { Chunked, Stream, + #[default] Auto, } -impl Default for UploadStrategy { - fn default() -> Self { - UploadStrategy::Auto - } -} - #[derive(Debug, Clone)] pub struct UploadProgress { pub bytes_uploaded: usize, @@ -145,7 +140,7 @@ impl UploadOptions { pub fn validate(&self) -> Result<()> { if let Some(chunk_size) = self.chunk_size { if chunk_size == 0 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "chunk_size", "Chunk size must be greater than 0", )); @@ -154,7 +149,7 @@ impl UploadOptions { if let Some(timeout) = self.timeout { if timeout == 0 { - return Err(CodexError::invalid_parameter( + return Err(StorageError::invalid_parameter( "timeout", "Timeout must be greater than 0", )); @@ -229,7 +224,7 @@ mod tests { assert_eq!(options.filepath, Some(PathBuf::from("/test/file.txt"))); assert_eq!(options.chunk_size, Some(2048)); assert_eq!(options.strategy, UploadStrategy::Chunked); - assert_eq!(options.verify, false); + assert!(!options.verify); assert_eq!(options.timeout, Some(600)); } diff --git a/src_build/bindings.rs b/src_build/bindings.rs new file mode 100644 index 0000000..4baf398 --- /dev/null +++ b/src_build/bindings.rs @@ -0,0 +1,83 @@ +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +/// Post-processes the generated bindings file to fix clippy warnings +fn post_process_bindings(bindings_file: &Path) { + let content = fs::read_to_string(bindings_file).expect("Failed to read bindings file"); + + // Fix empty line after outer attribute + // Replace "#[allow(non_snake_case)]\n\n" with "#[allow(non_snake_case)]\n" + let fixed_content = + content.replace("#[allow(non_snake_case)]\n\n", "#[allow(non_snake_case)]\n"); + + fs::write(bindings_file, fixed_content).expect("Failed to write fixed bindings"); +} + +/// Generates Rust FFI bindings from the prebuilt header file +pub fn generate_bindings(lib_dir: &Path) { + println!(" [BINDINGS] Starting generate_bindings"); + println!(" [BINDINGS] Library directory: {}", lib_dir.display()); + + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + println!(" [BINDINGS] Output directory: {}", out_path.display()); + + // The header file is at the root of the extracted directory + let libstorage_header_path = lib_dir.join("libstorage.h"); + println!( + " [BINDINGS] Header file path: {}", + libstorage_header_path.display() + ); + + if !libstorage_header_path.exists() { + println!(" [BINDINGS] ✗ Header file does not exist!"); + panic!( + "libstorage.h not found in prebuilt package at '{}'. \ + This should not happen - please report this issue.", + libstorage_header_path.display() + ); + } + println!(" [BINDINGS] ✓ Header file exists"); + + println!(" [BINDINGS] Configuring bindgen builder..."); + let builder = bindgen::Builder::default() + .header(libstorage_header_path.to_str().expect("Invalid path")) + .default_enum_style(bindgen::EnumVariation::Rust { + non_exhaustive: false, + }) + .generate_block(true) + .layout_tests(false) + .allowlist_function("storage_.*") + .allowlist_type("storage_.*") + .allowlist_var("storage_.*") + .allowlist_var("RET_.*") + .raw_line("#[allow(non_camel_case_types)]") + .raw_line("#[allow(non_snake_case)]") + .clang_arg("-D__STDC_VERSION__=201112L") + .clang_arg("-D__bool_true_false_are_defined=1") + .clang_arg("-includestdbool.h"); + println!(" [BINDINGS] ✓ Bindgen builder configured"); + + println!(" [BINDINGS] Generating bindings..."); + let bindings = builder.generate().expect("Unable to generate bindings"); + println!(" [BINDINGS] ✓ Bindings generated successfully"); + + let bindings_file = out_path.join("bindings.rs"); + println!( + " [BINDINGS] Writing bindings to: {}", + bindings_file.display() + ); + bindings + .write_to_file(&bindings_file) + .expect("Couldn't write bindings!"); + println!(" [BINDINGS] ✓ Bindings written successfully"); + + post_process_bindings(&bindings_file); + println!(" [BINDINGS] ✓ Bindings post-processed successfully"); + + println!( + "cargo:rerun-if-changed={}", + libstorage_header_path.display() + ); + println!(" [BINDINGS] ✓ generate_bindings completed successfully"); +} diff --git a/src_build/cache.rs b/src_build/cache.rs new file mode 100644 index 0000000..3e8b550 --- /dev/null +++ b/src_build/cache.rs @@ -0,0 +1,103 @@ +use std::env; +use std::fs; +use std::path::{Path, PathBuf}; + +pub const CACHE_DIR_NAME: &str = "storage-bindings"; +pub const FORCE_DOWNLOAD_ENV_VAR: &str = "STORAGE_BINDINGS_FORCE_DOWNLOAD"; +pub const CLEAN_CACHE_ENV_VAR: &str = "STORAGE_BINDINGS_CLEAN_CACHE"; + +/// Gets the base cache directory (platform-specific) +/// - Linux/macOS: ~/.cache/storage-bindings/ +/// - Windows: %LOCALAPPDATA%\storage-bindings\cache\ +pub fn get_cache_base_dir() -> Result> { + let cache_dir = dirs::cache_dir().ok_or("Failed to determine cache directory")?; + + Ok(cache_dir.join(CACHE_DIR_NAME)) +} + +/// Gets the version-specific cache directory +/// Structure: /// +pub fn get_version_cache_dir( + version: &str, + platform: &str, +) -> Result> { + let cache_base = get_cache_base_dir()?; + Ok(cache_base.join(version).join(platform)) +} + +/// Checks if force download is requested via environment variable +pub fn should_force_download() -> bool { + env::var(FORCE_DOWNLOAD_ENV_VAR).is_ok() +} + +/// Checks if cache cleanup is requested via environment variable +pub fn should_clean_cache() -> bool { + env::var(CLEAN_CACHE_ENV_VAR).is_ok() +} + +/// Cleans the entire cache directory +pub fn clean_cache() -> Result<(), Box> { + let cache_base = get_cache_base_dir()?; + + if cache_base.exists() { + println!( + " [CACHE] Removing cache directory: {}", + cache_base.display() + ); + fs::remove_dir_all(&cache_base)?; + println!(" [CACHE] ✓ Cache cleaned successfully"); + } else { + println!(" [CACHE] Cache directory does not exist, nothing to clean"); + } + + Ok(()) +} + +/// Copies files from cache to output directory +pub fn copy_from_cache(cache_dir: &Path, out_dir: &Path) -> Result<(), Box> { + println!(" [CACHE] Copying files from cache to OUT_DIR..."); + + for entry in fs::read_dir(cache_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let file_name = path + .file_name() + .and_then(|s| s.to_str()) + .ok_or("Invalid filename")?; + + let dest = out_dir.join(file_name); + fs::copy(&path, &dest)?; + println!(" [CACHE] Copied: {}", file_name); + } + } + + println!(" [CACHE] ✓ All files copied from cache"); + Ok(()) +} + +/// Validates that cache directory contains required files +pub fn validate_cache(cache_dir: &Path) -> Result<(), Box> { + if !cache_dir.exists() { + return Err("Cache directory does not exist".into()); + } + + // Check for at least one .a library file + let has_library = cache_dir + .read_dir()? + .filter_map(|e| e.ok()) + .any(|e| e.path().extension().is_some_and(|ext| ext == "a")); + + if !has_library { + return Err("No library files (.a) found in cache".into()); + } + + // Check for required header file + let libstorage_h = cache_dir.join("libstorage.h"); + if !libstorage_h.exists() { + return Err("Required header file libstorage.h not found in cache".into()); + } + + Ok(()) +} diff --git a/src_build/checksum.rs b/src_build/checksum.rs new file mode 100644 index 0000000..d8dfb28 --- /dev/null +++ b/src_build/checksum.rs @@ -0,0 +1,194 @@ +use sha2::{Digest, Sha256}; +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fs; +use std::io::Read; +use std::path::Path; + +/// Calculates SHA256 checksum of a file +pub fn calculate_sha256(file_path: &Path) -> Result> { + println!( + " [CHECKSUM] Calculating SHA256 for: {}", + file_path.display() + ); + + let mut file = fs::File::open(file_path)?; + let mut hasher = Sha256::new(); + let mut buffer = [0u8; 8192]; + let mut total_bytes = 0u64; + + loop { + let n = file.read(&mut buffer)?; + if n == 0 { + break; + } + hasher.update(&buffer[..n]); + total_bytes += n as u64; + } + + let checksum = hex::encode(hasher.finalize()); + println!(" [CHECKSUM] ✓ SHA256 calculated: {}", checksum); + println!(" [CHECKSUM] ✓ Total bytes processed: {}", total_bytes); + + Ok(checksum) +} + +/// Verifies the checksum of an archive file against an expected checksum +pub fn verify_archive_checksum( + archive_path: &Path, + expected_checksum: &str, +) -> Result<(), Box> { + println!(" [CHECKSUM] Verifying archive checksum..."); + println!(" [CHECKSUM] Archive path: {}", archive_path.display()); + println!(" [CHECKSUM] Expected checksum: {}", expected_checksum); + + let actual_checksum = calculate_sha256(archive_path)?; + + if actual_checksum != expected_checksum { + println!(" [CHECKSUM] ✗ Archive checksum mismatch!"); + println!(" [CHECKSUM] Expected: {}", expected_checksum); + println!(" [CHECKSUM] Actual: {}", actual_checksum); + return Err(format!( + "Archive checksum mismatch:\n Expected: {}\n Actual: {}", + expected_checksum, actual_checksum + ) + .into()); + } + + println!(" [CHECKSUM] ✓ Archive checksum verified"); + Ok(()) +} + +/// Parses a SHA256SUMS.txt file and returns a HashMap of filename -> checksum +pub fn parse_checksums_file( + path: &Path, +) -> Result, Box> { + println!(" [CHECKSUM] Parsing checksums file: {}", path.display()); + + let content = fs::read_to_string(path)?; + let mut checksums = HashMap::new(); + let mut line_count = 0; + + for line in content.lines() { + line_count += 1; + let line = line.trim(); + + // Skip empty lines and comments + if line.is_empty() || line.starts_with('#') { + continue; + } + + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 2 { + // Format: + checksums.insert(parts[1].to_string(), parts[0].to_string()); + } else { + println!( + " [CHECKSUM] ⚠ Skipping invalid line {}: {}", + line_count, line + ); + } + } + + println!(" [CHECKSUM] ✓ Parsed {} checksum entries", checksums.len()); + Ok(checksums) +} + +/// Verifies checksums for all files in a directory against SHA256SUMS.txt +pub fn verify_all_checksums(dir: &Path) -> Result<(), Box> { + println!(" [CHECKSUM] Starting comprehensive checksum verification"); + println!(" [CHECKSUM] Directory: {}", dir.display()); + + let checksums_file = dir.join("SHA256SUMS.txt"); + + if !checksums_file.exists() { + println!(" [CHECKSUM] ⚠ SHA256SUMS.txt not found, skipping checksum verification"); + return Ok(()); + } + + // Parse checksums file + let checksums = parse_checksums_file(&checksums_file)?; + + if checksums.is_empty() { + println!(" [CHECKSUM] ⚠ No checksums found in SHA256SUMS.txt"); + return Ok(()); + } + + // Verify each file + let mut verified_count = 0; + let mut failed_count = 0; + let mut skipped_count = 0; + + for entry in fs::read_dir(dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() && path.file_name() != Some(OsStr::new("SHA256SUMS.txt")) { + let file_name = path + .file_name() + .and_then(|s| s.to_str()) + .unwrap_or(""); + + if let Some(expected_checksum) = checksums.get(file_name) { + match verify_single_checksum(&path, expected_checksum) { + Ok(_) => { + println!(" [CHECKSUM] ✓ Checksum verified: {}", file_name); + verified_count += 1; + } + Err(e) => { + println!(" [CHECKSUM] ✗ Checksum failed: {} - {}", file_name, e); + failed_count += 1; + } + } + } else { + // File exists but no checksum in SHA256SUMS.txt + println!(" [CHECKSUM] ⚠ No checksum found for: {}", file_name); + skipped_count += 1; + } + } + } + + println!(" [CHECKSUM] Checksum verification summary:"); + println!(" [CHECKSUM] Verified: {}", verified_count); + println!(" [CHECKSUM] Failed: {}", failed_count); + println!(" [CHECKSUM] Skipped (no checksum): {}", skipped_count); + + if failed_count > 0 { + return Err(format!("{} files failed checksum verification", failed_count).into()); + } + + println!(" [CHECKSUM] ✓ All checksums verified successfully"); + Ok(()) +} + +/// Verifies a single file's checksum against an expected value +fn verify_single_checksum( + file_path: &Path, + expected_checksum: &str, +) -> Result<(), Box> { + let mut file = fs::File::open(file_path)?; + let mut hasher = Sha256::new(); + let mut buffer = [0u8; 8192]; + + loop { + let n = file.read(&mut buffer)?; + if n == 0 { + break; + } + hasher.update(&buffer[..n]); + } + + let actual_checksum = hex::encode(hasher.finalize()); + + if expected_checksum != actual_checksum { + return Err(format!( + "Checksum mismatch for {}: expected {}, got {}", + file_path.display(), + expected_checksum, + actual_checksum + ) + .into()); + } + + Ok(()) +} diff --git a/src_build/cmdline.rs b/src_build/cmdline.rs new file mode 100644 index 0000000..244a421 --- /dev/null +++ b/src_build/cmdline.rs @@ -0,0 +1,21 @@ +/// Compiles the cmdline_symbols.c file to provide missing Nim symbols +pub fn compile_cmdline_symbols() { + println!(" [CMDLINE] Starting compile_cmdline_symbols"); + + let source_file = "src_build/cmdline_symbols.c"; + println!(" [CMDLINE] Source file: {}", source_file); + + if std::path::Path::new(source_file).exists() { + println!(" [CMDLINE] ✓ Source file exists"); + } else { + println!(" [CMDLINE] ✗ Source file does not exist!"); + } + + println!(" [CMDLINE] Compiling with cc crate..."); + cc::Build::new() + .file(source_file) + .compile("cmdline_symbols"); + + println!(" [CMDLINE] ✓ Compilation completed successfully"); + println!(" [CMDLINE] ✓ Output library: cmdline_symbols"); +} diff --git a/src_build/cmdline_symbols.c b/src_build/cmdline_symbols.c new file mode 100644 index 0000000..4a2ec50 --- /dev/null +++ b/src_build/cmdline_symbols.c @@ -0,0 +1,12 @@ +// These symbols are normally defined in the Nim-generated main function, +// but when building as a library, they need to be defined explicitly. + +#include + +#ifdef __ANDROID__ +__attribute__((weak)) int cmdCount = 0; +__attribute__((weak)) char** cmdLine = NULL; +#else +int cmdCount = 0; +char** cmdLine = NULL; +#endif \ No newline at end of file diff --git a/src_build/download.rs b/src_build/download.rs new file mode 100644 index 0000000..026035e --- /dev/null +++ b/src_build/download.rs @@ -0,0 +1,65 @@ +use std::fs::File; +use std::io::{Read, Write}; +use std::path::PathBuf; + +use super::urls; + +/// Downloads a file to a specified path +pub fn download_file(url: &str, dest_path: &PathBuf) -> Result<(), Box> { + println!(" [DOWNLOAD] Starting download_file"); + println!(" [DOWNLOAD] URL: {}", url); + println!(" [DOWNLOAD] Destination: {}", dest_path.display()); + + println!(" [DOWNLOAD] Creating HTTP client..."); + let client = reqwest::blocking::Client::builder() + .user_agent(urls::USER_AGENT) + .timeout(std::time::Duration::from_secs( + urls::DOWNLOAD_TIMEOUT_SECONDS, + )) + .build()?; + println!(" [DOWNLOAD] ✓ HTTP client created"); + + println!(" [DOWNLOAD] Starting HTTP GET request..."); + let response = client.get(url).send()?; + println!(" [DOWNLOAD] ✓ HTTP response received"); + println!(" [DOWNLOAD] Status: {}", response.status()); + + if !response.status().is_success() { + return Err(format!("Download failed with status: {}", response.status()).into()); + } + + let total_size = response.content_length().unwrap_or(0); + println!(" [DOWNLOAD] Total size: {} bytes", total_size); + + println!(" [DOWNLOAD] Creating destination file..."); + let mut dest_file = File::create(dest_path)?; + println!(" [DOWNLOAD] ✓ Destination file created"); + + println!(" [DOWNLOAD] Copying response body to file..."); + let mut reader = response; + let mut buffer = vec![0u8; urls::DOWNLOAD_BUFFER_SIZE]; + let mut downloaded = 0u64; + + loop { + let bytes_read = reader.read(&mut buffer)?; + if bytes_read == 0 { + break; + } + dest_file.write_all(&buffer[..bytes_read])?; + downloaded += bytes_read as u64; + + if total_size > 0 { + let percent = (downloaded as f64 / total_size as f64) * 100.0; + println!( + " [DOWNLOAD] Progress: {:.1}% ({}/{} bytes)", + percent, downloaded, total_size + ); + } else { + println!(" [DOWNLOAD] Downloaded: {} bytes", downloaded); + } + } + + println!(" [DOWNLOAD] ✓ Copied {} bytes", downloaded); + println!(" [DOWNLOAD] ✓ download_file completed successfully"); + Ok(()) +} diff --git a/src_build/github.rs b/src_build/github.rs new file mode 100644 index 0000000..ac56fe4 --- /dev/null +++ b/src_build/github.rs @@ -0,0 +1,169 @@ +use serde::Deserialize; + +use super::urls; + +#[derive(Debug, Deserialize)] +pub struct GitHubRelease { + pub tag_name: String, + pub assets: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct GitHubAsset { + pub name: String, + pub browser_download_url: String, +} + +/// Fetches release information from GitHub API +pub fn fetch_release(version: &str) -> Result> { + println!(" [GITHUB] Starting fetch_release"); + println!(" [GITHUB] Version: {}", version); + + let url = if version == "latest" { + println!(" [GITHUB] Using latest release endpoint"); + urls::latest_release_url() + } else { + println!(" [GITHUB] Using tagged release endpoint"); + urls::tagged_release_url(version) + }; + + println!(" [GITHUB] URL: {}", url); + + println!(" [GITHUB] Creating HTTP client..."); + let client = reqwest::blocking::Client::builder() + .user_agent(urls::USER_AGENT) + .timeout(std::time::Duration::from_secs(urls::API_TIMEOUT_SECONDS)) + .build()?; + println!(" [GITHUB] ✓ HTTP client created"); + + println!(" [GITHUB] Sending GET request to GitHub API..."); + let response = client.get(&url).send()?; + println!(" [GITHUB] ✓ Response received"); + println!(" [GITHUB] Status: {}", response.status()); + + if !response.status().is_success() { + println!(" [GITHUB] ✗ GitHub API request failed"); + return Err(format!("GitHub API returned status: {}", response.status()).into()); + } + + println!(" [GITHUB] Parsing JSON response..."); + let release: GitHubRelease = response.json()?; + println!(" [GITHUB] ✓ JSON parsed successfully"); + println!(" [GITHUB] Tag name: {}", release.tag_name); + println!(" [GITHUB] Number of assets: {}", release.assets.len()); + + for (i, asset) in release.assets.iter().enumerate() { + println!(" [GITHUB] Asset {}: {}", i + 1, asset.name); + } + + println!(" [GITHUB] ✓ fetch_release completed successfully"); + Ok(release) +} + +/// Finds the matching asset for the given platform +pub fn find_matching_asset<'a>( + release: &'a GitHubRelease, + platform: &str, +) -> Option<&'a GitHubAsset> { + println!(" [GITHUB] Starting find_matching_asset"); + println!(" [GITHUB] Platform: {}", platform); + println!( + " [GITHUB] Total assets available: {}", + release.assets.len() + ); + + let search_pattern = format!("linux-{}", platform.replace("linux-", "")); + println!(" [GITHUB] Search pattern: {}", search_pattern); + + let matching_asset = release + .assets + .iter() + .find(|asset| asset.name.contains(&search_pattern)); + + match &matching_asset { + Some(asset) => { + println!(" [GITHUB] ✓ Found matching asset: {}", asset.name); + } + None => { + println!(" [GITHUB] ✗ No matching asset found"); + println!(" [GITHUB] Available assets:"); + for (i, asset) in release.assets.iter().enumerate() { + println!(" [GITHUB] {}: {}", i + 1, asset.name); + } + } + } + + println!(" [GITHUB] ✓ find_matching_asset completed"); + matching_asset +} + +/// Fetches the SHA256SUMS.txt file from a GitHub release +pub fn fetch_checksums_file(version: &str) -> Result> { + println!(" [GITHUB] Starting fetch_checksums_file"); + println!(" [GITHUB] Version: {}", version); + + let url = if version == "latest" { + println!(" [GITHUB] Using latest release endpoint"); + urls::latest_release_url() + } else { + println!(" [GITHUB] Using tagged release endpoint"); + urls::tagged_release_url(version) + }; + + println!(" [GITHUB] URL: {}", url); + + println!(" [GITHUB] Creating HTTP client..."); + let client = reqwest::blocking::Client::builder() + .user_agent(urls::USER_AGENT) + .timeout(std::time::Duration::from_secs(urls::API_TIMEOUT_SECONDS)) + .build()?; + println!(" [GITHUB] ✓ HTTP client created"); + + println!(" [GITHUB] Sending GET request to GitHub API..."); + let response = client.get(&url).send()?; + println!(" [GITHUB] ✓ Response received"); + println!(" [GITHUB] Status: {}", response.status()); + + if !response.status().is_success() { + println!(" [GITHUB] ✗ GitHub API request failed"); + return Err(format!("GitHub API returned status: {}", response.status()).into()); + } + + println!(" [GITHUB] Parsing JSON response..."); + let release: GitHubRelease = response.json()?; + println!(" [GITHUB] ✓ JSON parsed successfully"); + + // Find SHA256SUMS.txt asset + println!(" [GITHUB] Looking for SHA256SUMS.txt asset..."); + let checksums_asset = release + .assets + .iter() + .find(|asset| asset.name == "SHA256SUMS.txt") + .ok_or("SHA256SUMS.txt not found in release assets")?; + + println!(" [GITHUB] ✓ Found SHA256SUMS.txt asset"); + println!( + " [GITHUB] Download URL: {}", + checksums_asset.browser_download_url + ); + + // Download the checksums file content + println!(" [GITHUB] Downloading SHA256SUMS.txt content..."); + let checksums_response = client.get(&checksums_asset.browser_download_url).send()?; + + if !checksums_response.status().is_success() { + println!(" [GITHUB] ✗ Failed to download SHA256SUMS.txt"); + return Err(format!( + "Failed to download SHA256SUMS.txt: {}", + checksums_response.status() + ) + .into()); + } + + let content = checksums_response.text()?; + println!(" [GITHUB] ✓ SHA256SUMS.txt downloaded successfully"); + println!(" [GITHUB] Content length: {} bytes", content.len()); + + println!(" [GITHUB] ✓ fetch_checksums_file completed successfully"); + Ok(content) +} diff --git a/src_build/linker.rs b/src_build/linker.rs new file mode 100644 index 0000000..03d6f08 --- /dev/null +++ b/src_build/linker.rs @@ -0,0 +1,82 @@ +use std::path::PathBuf; + +/// Links against the prebuilt static library +pub fn link_prebuilt_library(lib_dir: &PathBuf) { + println!(" [LINKER] Starting link_prebuilt_library"); + println!(" [LINKER] Library directory: {}", lib_dir.display()); + + // Verify library directory exists + if lib_dir.exists() { + println!(" [LINKER] ✓ Library directory exists"); + + // List files in the directory + if let Ok(entries) = std::fs::read_dir(lib_dir) { + println!(" [LINKER] Files in library directory:"); + for entry in entries.flatten() { + let path = entry.path(); + let metadata = entry.metadata(); + if let Ok(meta) = metadata { + let size = meta.len(); + let file_type = if meta.is_file() { + "file" + } else if meta.is_dir() { + "dir" + } else { + "other" + }; + println!( + " [LINKER] - {} ({}, {} bytes)", + path.display(), + file_type, + size + ); + } else { + println!(" [LINKER] - {} (metadata unavailable)", path.display()); + } + } + } + } else { + println!(" [LINKER] ✗ Library directory does not exist!"); + } + + println!(" [LINKER] Setting link search path..."); + println!("cargo:rustc-link-search=native={}", lib_dir.display()); + println!(" [LINKER] ✓ Link search path set"); + + // Link each library separately + println!(" [LINKER] Linking static libraries:"); + println!(" [LINKER] - storage"); + println!("cargo:rustc-link-lib=static=storage"); + println!(" [LINKER] - natpmp"); + println!("cargo:rustc-link-lib=static=natpmp"); + println!(" [LINKER] - miniupnpc"); + println!("cargo:rustc-link-lib=static=miniupnpc"); + println!(" [LINKER] - circom_compat_ffi"); + println!("cargo:rustc-link-lib=static=circom_compat_ffi"); + println!(" [LINKER] - backtrace"); + println!("cargo:rustc-link-lib=static=backtrace"); + println!(" [LINKER] - libleopard"); + println!("cargo:rustc-link-lib=static=libleopard"); + println!(" [LINKER] ✓ Static libraries linked"); + + // System libraries required by the prebuilt library + println!(" [LINKER] Linking system libraries:"); + println!(" [LINKER] - stdc++"); + println!("cargo:rustc-link-lib=stdc++"); + println!(" [LINKER] - gomp (dylib)"); + println!("cargo:rustc-link-lib=dylib=gomp"); + println!(" [LINKER] ✓ System libraries linked"); + + // Linker flags + println!(" [LINKER] Setting linker flags:"); + println!(" [LINKER] - --allow-multiple-definition"); + println!("cargo:rustc-link-arg=-Wl,--allow-multiple-definition"); + println!(" [LINKER] - --defsym=__rust_probestack=0"); + println!("cargo:rustc-link-arg=-Wl,--defsym=__rust_probestack=0"); + println!(" [LINKER] - --whole-archive for static libraries"); + println!("cargo:rustc-link-arg=-Wl,--whole-archive"); + println!("cargo:rustc-link-arg=-Wl,--no-whole-archive"); + println!(" [LINKER] ✓ Linker flags set"); + + println!(" [LINKER] ✓ link_prebuilt_library completed successfully"); +} diff --git a/src_build/local.rs b/src_build/local.rs new file mode 100644 index 0000000..17a219a --- /dev/null +++ b/src_build/local.rs @@ -0,0 +1,65 @@ +use std::path::{Path, PathBuf}; + +use super::prebuilt; + +pub const LOCAL_LIBS_ENV_VAR: &str = "STORAGE_BINDINGS_LOCAL_LIBS"; + +pub const LIBSTORAGE_H: &str = "libstorage.h"; + +/// Attempts to use local libraries if environment variable is set +/// Returns Ok with the output directory path if successful, Err otherwise +pub fn try_local_development_mode(out_dir: &Path) -> Result> { + let local_libs_path = match std::env::var(LOCAL_LIBS_ENV_VAR) { + Ok(path) => path, + Err(_) => { + prebuilt::log_info("Local development mode not enabled"); + return Err("Local development mode not enabled".into()); + } + }; + + prebuilt::log_info("🚀 LOCAL DEVELOPMENT MODE DETECTED"); + prebuilt::log_info(&format!("Local libs path: {}", local_libs_path)); + + let local_path = PathBuf::from(&local_libs_path); + + if !local_path.exists() { + return Err(format!( + "Local library path does not exist: {}. \ + Please check the {} environment variable.", + local_libs_path, LOCAL_LIBS_ENV_VAR + ) + .into()); + } + + prebuilt::validate_required_files(&local_path)?; + + prebuilt::log_info(&format!( + "✓ Using local libraries from: {}", + local_libs_path + )); + prebuilt::log_info("✓ All required files found"); + + copy_all_files(&local_path, out_dir)?; + + prebuilt::log_info("✓ Local libraries copied successfully"); + Ok(out_dir.to_path_buf()) +} + +/// Copies all files from local path to output directory +fn copy_all_files(local_path: &Path, out_dir: &Path) -> Result<(), Box> { + prebuilt::log_info("Copying all files from local path to OUT_DIR..."); + + for entry in std::fs::read_dir(local_path)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let file_name = path.file_name().unwrap().to_string_lossy().to_string(); + let dest = out_dir.join(&file_name); + std::fs::copy(&path, &dest)?; + prebuilt::log_info(&format!(" Copied: {}", file_name)); + } + } + + Ok(()) +} diff --git a/src_build/mod.rs b/src_build/mod.rs new file mode 100644 index 0000000..069f6a2 --- /dev/null +++ b/src_build/mod.rs @@ -0,0 +1,13 @@ +pub mod bindings; +pub mod cache; +pub mod checksum; +pub mod cmdline; +pub mod download; +pub mod github; +pub mod linker; +pub mod local; +pub mod prebuilt; +pub mod remote; +pub mod targets; +pub mod urls; +pub mod version; diff --git a/src_build/prebuilt.rs b/src_build/prebuilt.rs new file mode 100644 index 0000000..d10e20f --- /dev/null +++ b/src_build/prebuilt.rs @@ -0,0 +1,54 @@ +use std::path::{Path, PathBuf}; + +use super::local; +use super::remote; + +pub use super::local::LIBSTORAGE_H; + +/// Ensures prebuilt binary is available in OUT_DIR +/// Downloads and extracts if not cached +pub fn ensure_prebuilt_binary( + out_dir: &Path, + target: &str, +) -> Result> { + log_info("Starting ensure_prebuilt_binary"); + log_info(&format!("Target: {}", target)); + log_info(&format!("Output directory: {}", out_dir.display())); + + if let Ok(result) = local::try_local_development_mode(out_dir) { + return Ok(result); + } + + remote::download_from_github(out_dir, target) +} + +/// Validates that required files exist in the given path +pub fn validate_required_files(path: &Path) -> Result<(), Box> { + // Check for at least one .a library file + let has_library = path + .read_dir()? + .filter_map(|e| e.ok()) + .any(|e| e.path().extension().is_some_and(|ext| ext == "a")); + + if !has_library { + return Err("No library files (.a) found in the directory.".into()); + } + + // Check for required header file + let libstorage_h = path.join(LIBSTORAGE_H); + if !libstorage_h.exists() { + return Err(format!( + "Required header file not found: {}. \ + Please ensure the folder contains {}", + libstorage_h.display(), + LIBSTORAGE_H + ) + .into()); + } + + Ok(()) +} + +pub fn log_info(message: &str) { + println!(" [PREBUILT] {}", message); +} diff --git a/src_build/remote.rs b/src_build/remote.rs new file mode 100644 index 0000000..5c80510 --- /dev/null +++ b/src_build/remote.rs @@ -0,0 +1,228 @@ +use std::fs; +use std::path::{Path, PathBuf}; + +use super::cache; +use super::checksum; +use super::download; +use super::github; +use super::prebuilt; +use super::targets; +use super::version; + +/// Downloads prebuilt binaries from GitHub +pub fn download_from_github( + out_dir: &Path, + target: &str, +) -> Result> { + let platform = targets::map_target_to_platform(target).ok_or_else(|| { + let supported = targets::supported_targets().join(", "); + format!( + "Unsupported target: {}. Supported platforms: {}.", + target, supported + ) + })?; + + prebuilt::log_info(&format!("Mapped target to platform: {}", platform)); + + // Get the version we're using + let release_version = version::get_release_version()?; + + // Check if force download is requested + if cache::should_force_download() { + prebuilt::log_info("Force download requested, skipping cache"); + download_and_extract_binaries(out_dir, platform, &release_version)?; + prebuilt::validate_required_files(out_dir)?; + return Ok(out_dir.to_path_buf()); + } + + // Try to use cached files + if let Ok(result) = try_use_cached_files(out_dir, &release_version, platform) { + return Ok(result); + } + + // Download and cache + download_and_extract_binaries(out_dir, platform, &release_version)?; + prebuilt::validate_required_files(out_dir)?; + + // Save to global cache + save_to_cache(out_dir, &release_version, platform)?; + + prebuilt::log_info("✓ Successfully extracted and cached prebuilt binaries"); + prebuilt::log_info(&format!("✓ Returning directory: {}", out_dir.display())); + Ok(out_dir.to_path_buf()) +} + +/// Attempts to use cached files if they exist and are valid +fn try_use_cached_files( + out_dir: &Path, + version: &str, + platform: &str, +) -> Result> { + let cache_dir = cache::get_version_cache_dir(version, platform)?; + + prebuilt::log_info("Checking cache:"); + prebuilt::log_info(&format!( + " Cache directory: {} (exists: {})", + cache_dir.display(), + cache_dir.exists() + )); + + if !cache_dir.exists() { + return Err("Cache directory not found".into()); + } + + // Validate cache contents + cache::validate_cache(&cache_dir)?; + + prebuilt::log_info("✓ Using cached prebuilt binaries"); + + // Verify checksums of cached files + prebuilt::log_info("Verifying checksums of cached files..."); + if let Err(e) = checksum::verify_all_checksums(&cache_dir) { + prebuilt::log_info(&format!( + "⚠ Checksum verification failed for cached files: {}", + e + )); + prebuilt::log_info("Re-downloading prebuilt binaries..."); + // Remove invalid cache + let _ = fs::remove_dir_all(&cache_dir); + return Err("Checksum verification failed".into()); + } + prebuilt::log_info("✓ Checksum verification passed"); + + // Copy from cache to OUT_DIR + cache::copy_from_cache(&cache_dir, out_dir)?; + + prebuilt::log_info(&format!( + "✓ Returning cached directory: {}", + out_dir.display() + )); + Ok(out_dir.to_path_buf()) +} + +/// Downloads and extracts binaries from GitHub +fn download_and_extract_binaries( + out_dir: &Path, + platform: &str, + version: &str, +) -> Result<(), Box> { + prebuilt::log_info(&format!("Getting release version: {}", version)); + + prebuilt::log_info("Fetching release from GitHub..."); + let release = github::fetch_release(version)?; + prebuilt::log_info(&format!("✓ Release fetched: {}", release.tag_name)); + prebuilt::log_info(&format!(" Number of assets: {}", release.assets.len())); + + prebuilt::log_info(&format!( + "Looking for asset matching platform: {}", + platform + )); + let asset = github::find_matching_asset(&release, platform).ok_or_else(|| { + format!( + "No prebuilt binary found for platform: {} in release: {}. \ + Please check the GitHub releases page for available platforms.", + platform, release.tag_name + ) + })?; + + prebuilt::log_info("✓ Found matching asset:"); + prebuilt::log_info(&format!(" Name: {}", asset.name)); + prebuilt::log_info(&format!(" Download URL: {}", asset.browser_download_url)); + + // Download to temporary location + prebuilt::log_info("Downloading archive to temporary location..."); + let temp_archive = out_dir.join(format!("{}.tar.gz", platform)); + download::download_file(&asset.browser_download_url, &temp_archive)?; + prebuilt::log_info("✓ Archive downloaded to temporary location"); + + // Fetch SHA256SUMS.txt to get expected checksum for the archive + prebuilt::log_info("Fetching SHA256SUMS.txt from GitHub..."); + let checksums_content = github::fetch_checksums_file(version)?; + + // Parse checksums to find the expected checksum for this archive + prebuilt::log_info(&format!("Looking for checksum for: {}", asset.name)); + let expected_checksum = checksums_content + .lines() + .find_map(|line| { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 2 && parts[1] == asset.name { + Some(parts[0].to_string()) + } else { + None + } + }) + .ok_or_else(|| format!("Checksum not found for {} in SHA256SUMS.txt", asset.name))?; + + prebuilt::log_info(&format!("✓ Found expected checksum: {}", expected_checksum)); + + // Verify archive before extraction + prebuilt::log_info("Verifying archive checksum before extraction..."); + checksum::verify_archive_checksum(&temp_archive, &expected_checksum)?; + prebuilt::log_info("✓ Archive checksum verified"); + + // Extract the archive + prebuilt::log_info("Extracting archive..."); + extract_archive(&temp_archive, out_dir)?; + prebuilt::log_info("✓ Archive extracted"); + + // Clean up temporary archive + prebuilt::log_info("Cleaning up temporary archive..."); + fs::remove_file(&temp_archive)?; + prebuilt::log_info("✓ Temporary archive removed"); + + // Verify all extracted files + prebuilt::log_info("Verifying checksums of extracted files..."); + checksum::verify_all_checksums(out_dir)?; + prebuilt::log_info("✓ All checksums verified"); + + Ok(()) +} + +/// Saves downloaded files to global cache +fn save_to_cache( + out_dir: &Path, + version: &str, + platform: &str, +) -> Result<(), Box> { + let cache_dir = cache::get_version_cache_dir(version, platform)?; + + prebuilt::log_info(&format!("Saving to cache: {}", cache_dir.display())); + + // Create cache directory if it doesn't exist + fs::create_dir_all(&cache_dir)?; + + // Copy all files from OUT_DIR to cache + for entry in fs::read_dir(out_dir)? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() { + let file_name = path + .file_name() + .and_then(|s| s.to_str()) + .ok_or("Invalid filename")?; + + let dest = cache_dir.join(file_name); + fs::copy(&path, &dest)?; + prebuilt::log_info(&format!(" Cached: {}", file_name)); + } + } + + prebuilt::log_info("✓ Files saved to cache"); + Ok(()) +} + +/// Extracts a tar.gz archive to a directory +fn extract_archive(archive_path: &Path, dest_dir: &Path) -> Result<(), Box> { + prebuilt::log_info(&format!("Extracting archive: {}", archive_path.display())); + prebuilt::log_info(&format!("Destination: {}", dest_dir.display())); + + let file = fs::File::open(archive_path)?; + let gz_decoder = flate2::read::GzDecoder::new(file); + let mut tar_archive = tar::Archive::new(gz_decoder); + + tar_archive.unpack(dest_dir)?; + + prebuilt::log_info("✓ Archive extraction completed"); + Ok(()) +} diff --git a/src_build/targets.rs b/src_build/targets.rs new file mode 100644 index 0000000..656586d --- /dev/null +++ b/src_build/targets.rs @@ -0,0 +1,32 @@ +/// Supported Rust target triples and their corresponding platform identifiers +/// +/// This mapping defines which Rust target triples are supported and what +/// platform identifier to use when downloading prebuilt binaries. +/// +/// To add support for a new platform: +/// 1. Add an entry to this mapping +/// 2. Ensure the corresponding prebuilt binary exists in the logos-storage-nim-bin GitHub releases +pub const SUPPORTED_TARGETS: &[(&str, &str)] = &[ + ("x86_64-unknown-linux-gnu", "linux-amd64"), + ("aarch64-unknown-linux-gnu", "linux-arm64"), + ("aarch64-apple-darwin", "darwin-arm64"), + ("x86_64-apple-darwin", "darwin-amd64"), +]; + +/// Returns a list of all supported target triples +pub fn supported_targets() -> Vec<&'static str> { + SUPPORTED_TARGETS + .iter() + .map(|(target, _)| *target) + .collect() +} + +/// Maps a Rust target triple to its platform identifier +/// +/// Returns `None` if the target is not supported +pub fn map_target_to_platform(target: &str) -> Option<&'static str> { + SUPPORTED_TARGETS + .iter() + .find(|(t, _)| *t == target) + .map(|(_, platform)| *platform) +} diff --git a/src_build/urls.rs b/src_build/urls.rs new file mode 100644 index 0000000..1e59748 --- /dev/null +++ b/src_build/urls.rs @@ -0,0 +1,33 @@ +pub const GITHUB_REPO_OWNER: &str = "nipsysdev"; + +pub const GITHUB_REPO_NAME: &str = "logos-storage-nim-bin"; + +pub const GITHUB_API_BASE: &str = "https://api.github.com/repos"; + +/// User agent for HTTP requests +pub const USER_AGENT: &str = "storage-rust-bindings"; + +/// HTTP timeout for API requests (in seconds) +pub const API_TIMEOUT_SECONDS: u64 = 30; + +/// HTTP timeout for file downloads (in seconds) +pub const DOWNLOAD_TIMEOUT_SECONDS: u64 = 900; // 15 minutes + +/// Download buffer size in bytes +pub const DOWNLOAD_BUFFER_SIZE: usize = 8192; // 8KB + +/// Constructs the GitHub API URL for the latest release +pub fn latest_release_url() -> String { + format!( + "{}/{}/{}/releases/latest", + GITHUB_API_BASE, GITHUB_REPO_OWNER, GITHUB_REPO_NAME + ) +} + +/// Constructs the GitHub API URL for a specific tagged release +pub fn tagged_release_url(version: &str) -> String { + format!( + "{}/{}/{}/releases/tags/{}", + GITHUB_API_BASE, GITHUB_REPO_OWNER, GITHUB_REPO_NAME, version + ) +} diff --git a/src_build/version.rs b/src_build/version.rs new file mode 100644 index 0000000..d2e7010 --- /dev/null +++ b/src_build/version.rs @@ -0,0 +1,79 @@ +use std::env; +use std::fs; +use std::path::PathBuf; + +pub const STORAGE_VERSION_VAR: &str = "LOGOS_STORAGE_VERSION"; + +/// Gets the release version to use, with priority: +/// 1. Environment variable +/// 2. Cargo.toml metadata +/// 3. "latest" (default) +pub fn get_release_version() -> Result> { + println!(" [VERSION] Starting get_release_version"); + + // Check for environment variable override (highest priority) + println!( + " [VERSION] Checking environment variable {}...", + STORAGE_VERSION_VAR + ); + if let Ok(version) = env::var(STORAGE_VERSION_VAR) { + println!(" [VERSION] ✓ Found version from environment: {}", version); + println!( + " [VERSION] ✓ Using pinned version from environment: {}", + version + ); + return Ok(version); + } + println!(" [VERSION] Environment variable not set"); + + // Check for Cargo.toml metadata + println!(" [VERSION] Checking Cargo.toml metadata..."); + if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") { + let manifest_path = PathBuf::from(manifest_dir).join("Cargo.toml"); + println!(" [VERSION] Manifest path: {}", manifest_path.display()); + + if let Ok(content) = fs::read_to_string(&manifest_path) { + println!(" [VERSION] ✓ Cargo.toml read successfully"); + if let Some(version) = parse_metadata_version(&content) { + println!(" [VERSION] ✓ Found version from metadata: {}", version); + println!( + " [VERSION] ✓ Using pinned version from Cargo.toml metadata: {}", + version + ); + return Ok(version); + } else { + println!(" [VERSION] No version found in metadata"); + } + } else { + println!(" [VERSION] ✗ Failed to read Cargo.toml"); + } + } else { + println!(" [VERSION] ✗ CARGO_MANIFEST_DIR not set"); + } + + // Default to latest release + println!(" [VERSION] ✓ Using latest release"); + println!(" [VERSION] ✓ get_release_version completed successfully"); + Ok("latest".to_string()) +} + +/// Parses the libstorage version from Cargo.toml metadata +pub fn parse_metadata_version(cargo_toml: &str) -> Option { + cargo_toml + .lines() + .find(|line| line.contains("[package.metadata.prebuilt]")) + .and_then(|_| { + cargo_toml + .lines() + .skip_while(|line| !line.contains("[package.metadata.prebuilt]")) + .skip(1) + .take_while(|line| !line.starts_with('[')) + .find(|line| line.trim().starts_with("libstorage")) + .and_then(|line| { + line.split('=') + .nth(1) + .map(|v| v.trim().trim_matches('"').to_string()) + .filter(|s| !s.is_empty()) + }) + }) +} diff --git a/tests/basic_usage.rs b/tests/basic_usage.rs index f56906a..49aac34 100644 --- a/tests/basic_usage.rs +++ b/tests/basic_usage.rs @@ -1,14 +1,14 @@ -//! Basic usage integration test for the Codex Rust bindings +//! Basic usage integration test for the Storage Rust bindings //! -//! This test demonstrates how to create a Codex node, start it, +//! This test demonstrates how to create a Storage node, start it, //! upload a file, download it, and then clean up. -use codex_bindings::{ - download_stream, upload_file, CodexConfig, CodexNode, DownloadStreamOptions, LogLevel, - UploadOptions, -}; use std::fs::File; use std::io::Write; +use storage_bindings::{ + download_stream, upload_file, DownloadStreamOptions, LogLevel, StorageConfig, StorageNode, + UploadOptions, +}; use tempfile::tempdir; #[tokio::test] @@ -16,7 +16,7 @@ async fn test_basic_usage() -> Result<(), Box> { // Initialize logging let _ = env_logger::try_init(); - println!("Codex Rust Bindings - Basic Usage Test"); + println!("Storage Rust Bindings - Basic Usage Test"); println!("====================================="); // Create a temporary directory for our test @@ -27,25 +27,25 @@ async fn test_basic_usage() -> Result<(), Box> { // Create a test file to upload println!("Creating test file..."); let mut file = File::create(&file_path)?; - file.write_all(b"Hello, Codex! This is a test file for the Rust bindings.")?; + file.write_all(b"Hello, Storage! This is a test file for the Rust bindings.")?; file.sync_all()?; println!("Test file created at: {}", file_path.display()); - // Create a Codex configuration - println!("Creating Codex configuration..."); - let config = CodexConfig::new() + // Create a Storage configuration + println!("Creating Storage configuration..."); + let config = StorageConfig::new() .log_level(LogLevel::Info) - .data_dir(temp_dir.path().join("codex_data")) + .data_dir(temp_dir.path().join("storage_data")) .storage_quota(100 * 1024 * 1024) // 100 MB .max_peers(50) .discovery_port(8090); - // Create a new Codex node - println!("Creating Codex node..."); - let mut node = CodexNode::new(config)?; + // Create a new Storage node + println!("Creating Storage node..."); + let mut node = StorageNode::new(config)?; // Start the node - println!("Starting Codex node..."); + println!("Starting Storage node..."); node.start()?; println!("Node started successfully!"); @@ -104,11 +104,11 @@ async fn test_basic_usage() -> Result<(), Box> { println!("✓ Content verification successful!"); // Stop and destroy the node - println!("Stopping Codex node..."); + println!("Stopping Storage node..."); node.stop()?; println!("Node stopped."); - println!("Destroying Codex node..."); + println!("Destroying Storage node..."); node.destroy()?; println!("Node destroyed."); diff --git a/tests/chunk_operations.rs b/tests/chunk_operations.rs index 2654add..d0a07fb 100644 --- a/tests/chunk_operations.rs +++ b/tests/chunk_operations.rs @@ -1,6 +1,6 @@ -use codex_bindings::{ +use storage_bindings::{ download_cancel, download_chunk, download_init, upload_cancel, upload_chunk, upload_finalize, - upload_init, CodexConfig, CodexNode, LogLevel, UploadOptions, + upload_init, LogLevel, StorageConfig, StorageNode, UploadOptions, }; use tempfile::tempdir; @@ -8,25 +8,25 @@ use tempfile::tempdir; async fn test_chunk_operations() -> Result<(), Box> { let _ = env_logger::try_init(); - println!("Codex Rust Bindings - Chunk Operations Test"); + println!("Storage Rust Bindings - Chunk Operations Test"); println!("==========================================="); let temp_dir = tempdir()?; - println!("Creating Codex configuration..."); - let config = CodexConfig::new() + println!("Creating Storage configuration..."); + let config = StorageConfig::new() .log_level(LogLevel::Error) - .data_dir(temp_dir.path().join("codex_data")) + .data_dir(temp_dir.path().join("storage_data")) .storage_quota(100 * 1024 * 1024) .block_retries(3000) .discovery_port(8094); - println!("Creating and starting Codex node..."); - let mut node = CodexNode::new(config)?; + println!("Creating and starting Storage node..."); + let mut node = StorageNode::new(config)?; node.start()?; println!("Node started successfully!"); - let test_data = b"Hello, Codex! This is a test file for chunk-based upload. "; + let test_data = b"Hello, Storage! This is a test file for chunk-based upload. "; let test_data2 = b"It contains multiple chunks that will be uploaded separately. "; let test_data3 = b"This demonstrates the chunk-based upload functionality."; @@ -62,11 +62,11 @@ async fn test_chunk_operations() -> Result<(), Box> { println!(" CID: {}", cid); println!("\n=== Verifying Upload ==="); - let exists = codex_bindings::exists(&node, &cid).await?; + let exists = storage_bindings::exists(&node, &cid).await?; assert!(exists, "Content should exist after upload"); println!("Content exists: {}", exists); - let manifest = codex_bindings::fetch(&node, &cid).await?; + let manifest = storage_bindings::fetch(&node, &cid).await?; println!("Manifest information:"); println!(" CID: {}", manifest.cid); println!(" Size: {} bytes", manifest.dataset_size); @@ -76,7 +76,7 @@ async fn test_chunk_operations() -> Result<(), Box> { println!("\n=== Chunk-based Download ==="); println!("Initializing download..."); - let download_options = codex_bindings::DownloadOptions::new(&cid); + let download_options = storage_bindings::DownloadOptions::new(&cid); download_init(&node, &cid, &download_options).await?; println!("Download initialized for CID: {}", cid); @@ -180,7 +180,7 @@ async fn test_chunk_operations() -> Result<(), Box> { println!("✓ Small chunks upload finalized: {}", small_cid); println!("\n=== Final Storage Information ==="); - let space_info = codex_bindings::space(&node).await?; + let space_info = storage_bindings::space(&node).await?; println!("Storage usage:"); println!(" Used: {} bytes", space_info.quota_used_bytes); println!( @@ -189,7 +189,7 @@ async fn test_chunk_operations() -> Result<(), Box> { ); println!(" Total blocks: {}", space_info.total_blocks); - let manifests = codex_bindings::manifests(&node).await?; + let manifests = storage_bindings::manifests(&node).await?; println!("Total manifests: {}", manifests.len()); assert!( manifests.len() >= 2, diff --git a/tests/debug_operations.rs b/tests/debug_operations.rs index d2119fa..649ae38 100644 --- a/tests/debug_operations.rs +++ b/tests/debug_operations.rs @@ -1,12 +1,12 @@ -//! Debug operations integration test for the Codex Rust bindings +//! Debug operations integration test for the Storage Rust bindings //! //! This test demonstrates how to use debug operations: //! - Get node debug information //! - Update log levels //! - Get peer debug information -use codex_bindings::debug::LogLevel; -use codex_bindings::{CodexConfig, CodexNode}; +use storage_bindings::debug::LogLevel; +use storage_bindings::{StorageConfig, StorageNode}; use tempfile::tempdir; #[tokio::test] @@ -14,29 +14,28 @@ async fn test_debug_operations() -> Result<(), Box> { // Initialize logging let _ = env_logger::try_init(); - println!("Codex Rust Bindings - Debug Operations Test"); + println!("Storage Rust Bindings - Debug Operations Test"); println!("==========================================="); // Create a temporary directory for our test let temp_dir = tempdir()?; - // Create a Codex configuration - println!("Creating Codex configuration..."); - let config = CodexConfig::new() - .log_level(codex_bindings::LogLevel::Info) - .data_dir(temp_dir.path().join("codex_data")) - .storage_quota(100 * 1024 * 1024) // 100 MB + // Create a Storage configuration + println!("Creating Storage configuration..."); + let config = StorageConfig::new() + .log_level(storage_bindings::LogLevel::Info) + .data_dir(temp_dir.path().join("storage_data")) .discovery_port(8095); - // Create and start a Codex node - println!("Creating and starting Codex node..."); - let mut node = CodexNode::new(config)?; + // Create and start a Storage node + println!("Creating and starting Storage node..."); + let mut node = StorageNode::new(config)?; node.start()?; println!("Node started successfully!"); // Get initial debug information println!("\n=== Initial Debug Information ==="); - let debug_info = codex_bindings::debug(&node).await?; + let debug_info = storage_bindings::debug(&node).await?; println!("Peer ID: {}", debug_info.peer_id()); println!("Addresses: {:?}", debug_info.addrs); println!("SPR: {}", debug_info.spr); @@ -71,13 +70,13 @@ async fn test_debug_operations() -> Result<(), Box> { for log_level in log_levels { println!("Setting log level to: {:?}", log_level); - let update_result = codex_bindings::update_log_level(&node, log_level).await; + let update_result = storage_bindings::update_log_level(&node, log_level).await; match update_result { Ok(()) => { println!(" ✓ Successfully updated log level to {:?}", log_level); // Verify the change by getting debug info again - let debug_info = codex_bindings::debug(&node).await?; + let debug_info = storage_bindings::debug(&node).await?; println!(" Debug info retrieved successfully after log level change"); println!(" Peer ID: {}", debug_info.peer_id()); println!(" Address count: {}", debug_info.address_count()); @@ -89,7 +88,7 @@ async fn test_debug_operations() -> Result<(), Box> { } // Reset to Info level for further testing - codex_bindings::update_log_level(&node, LogLevel::Info).await?; + storage_bindings::update_log_level(&node, LogLevel::Info).await?; // Test peer debug information println!("\n=== Testing Peer Debug Information ==="); @@ -102,7 +101,7 @@ async fn test_debug_operations() -> Result<(), Box> { for peer_id in test_peer_ids { println!("Getting debug info for peer: {}", peer_id); - let peer_record = codex_bindings::peer_debug(&node, peer_id).await; + let peer_record = storage_bindings::peer_debug(&node, peer_id).await; match peer_record { Ok(record) => { println!(" ✓ Successfully retrieved peer debug info:"); @@ -143,12 +142,12 @@ async fn test_debug_operations() -> Result<(), Box> { // Test invalid peer ID println!("\n=== Testing Invalid Peer ID ==="); - let empty_peer_result = codex_bindings::peer_debug(&node, "").await; + let empty_peer_result = storage_bindings::peer_debug(&node, "").await; assert!(empty_peer_result.is_err(), "Should fail with empty peer ID"); println!(" ✓ Correctly failed with empty peer ID"); // Test whitespace-only peer ID - let whitespace_peer_result = codex_bindings::peer_debug(&node, " \t\n ").await; + let whitespace_peer_result = storage_bindings::peer_debug(&node, " \t\n ").await; assert!( whitespace_peer_result.is_err(), "Should fail with whitespace-only peer ID" @@ -157,16 +156,16 @@ async fn test_debug_operations() -> Result<(), Box> { // Test debug operations without starting node println!("\n=== Testing Debug Operations Without Starting Node ==="); - let config2 = CodexConfig::new() - .log_level(codex_bindings::LogLevel::Error) - .data_dir(temp_dir.path().join("codex_data2")) + let config2 = StorageConfig::new() + .log_level(storage_bindings::LogLevel::Error) + .data_dir(temp_dir.path().join("storage_data2")) .discovery_port(8096); - let node2 = CodexNode::new(config2)?; + let node2 = StorageNode::new(config2)?; // Don't start the node // These should work even if the node is not started - let debug_info_result = codex_bindings::debug(&node2).await; + let debug_info_result = storage_bindings::debug(&node2).await; match debug_info_result { Ok(info) => { println!(" ✓ Debug info works without starting node:"); @@ -176,13 +175,13 @@ async fn test_debug_operations() -> Result<(), Box> { Err(e) => println!(" ✗ Debug info failed without starting node: {}", e), } - let update_result = codex_bindings::update_log_level(&node2, LogLevel::Debug).await; + let update_result = storage_bindings::update_log_level(&node2, LogLevel::Debug).await; match update_result { Ok(()) => println!(" ✓ Log level update works without starting node"), Err(e) => println!(" ✗ Log level update failed without starting node: {}", e), } - let peer_debug_result = codex_bindings::peer_debug(&node2, "12D3KooWTestPeer").await; + let peer_debug_result = storage_bindings::peer_debug(&node2, "12D3KooWTestPeer").await; match peer_debug_result { Ok(_) => println!(" ✓ Peer debug works without starting node"), Err(e) => println!(" ✗ Peer debug failed without starting node: {}", e), @@ -193,10 +192,10 @@ async fn test_debug_operations() -> Result<(), Box> { // Test concurrent debug operations println!("\n=== Testing Concurrent Debug Operations ==="); - let debug_future1 = codex_bindings::debug(&node); - let debug_future2 = codex_bindings::debug(&node); - let peer_debug_future1 = codex_bindings::peer_debug(&node, "12D3KooWTestPeer1"); - let peer_debug_future2 = codex_bindings::peer_debug(&node, "12D3KooWTestPeer2"); + let debug_future1 = storage_bindings::debug(&node); + let debug_future2 = storage_bindings::debug(&node); + let peer_debug_future1 = storage_bindings::peer_debug(&node, "12D3KooWTestPeer1"); + let peer_debug_future2 = storage_bindings::peer_debug(&node, "12D3KooWTestPeer2"); let (debug_result1, debug_result2, peer_debug_result1, peer_debug_result2) = tokio::join!( debug_future1, @@ -225,7 +224,7 @@ async fn test_debug_operations() -> Result<(), Box> { // Get final debug information println!("\n=== Final Debug Information ==="); - let final_debug_info = codex_bindings::debug(&node).await?; + let final_debug_info = storage_bindings::debug(&node).await?; println!("Final node state:"); println!(" Peer ID: {}", final_debug_info.peer_id()); println!(" Address count: {}", final_debug_info.address_count()); diff --git a/tests/mod.rs b/tests/mod.rs index 9850126..5cfe5d2 100644 --- a/tests/mod.rs +++ b/tests/mod.rs @@ -1,4 +1,4 @@ -//! Integration tests for the Codex Rust bindings +//! Integration tests for the Storage Rust bindings //! //! Available tests: //! - basic_usage: Basic upload/download functionality diff --git a/tests/p2p_networking.rs b/tests/p2p_networking.rs index d39ca3a..67eb95a 100644 --- a/tests/p2p_networking.rs +++ b/tests/p2p_networking.rs @@ -1,11 +1,11 @@ -//! P2P networking integration test for the Codex Rust bindings +//! P2P networking integration test for the Storage Rust bindings //! //! This test demonstrates how to use P2P operations: //! - Connect to peers //! - Get peer information //! - Debug peer connections -use codex_bindings::{CodexConfig, CodexNode, LogLevel}; +use storage_bindings::{LogLevel, StorageConfig, StorageNode}; use tempfile::tempdir; #[tokio::test] @@ -13,24 +13,24 @@ async fn test_p2p_networking() -> Result<(), Box> { // Initialize logging let _ = env_logger::try_init(); - println!("Codex Rust Bindings - P2P Networking Test"); + println!("Storage Rust Bindings - P2P Networking Test"); println!("=========================================="); // Create a temporary directory for our test let temp_dir = tempdir()?; - // Create a minimal Codex configuration - println!("Creating Codex configuration..."); - let config = CodexConfig::new() + // Create a minimal Storage configuration + println!("Creating Storage configuration..."); + let config = StorageConfig::new() .log_level(LogLevel::Error) - .data_dir(temp_dir.path().join("codex_data")) + .data_dir(temp_dir.path().join("storage_data")) .max_peers(50) .block_retries(3000) .discovery_port(8091); - // Create and start a Codex node - println!("Creating and starting Codex node..."); - let mut node = CodexNode::new(config)?; + // Create and start a Storage node + println!("Creating and starting Storage node..."); + let mut node = StorageNode::new(config)?; node.start()?; println!("Node started successfully!"); @@ -49,7 +49,7 @@ async fn test_p2p_networking() -> Result<(), Box> { println!("\n=== P2P Operations ==="); // Get our own peer ID using the P2P function - let our_peer_id = codex_bindings::get_peer_id(&node).await?; + let our_peer_id = storage_bindings::get_peer_id(&node).await?; println!("Peer ID from P2P function: {}", our_peer_id); assert_eq!(peer_id, our_peer_id, "Peer IDs should match"); @@ -67,7 +67,7 @@ async fn test_p2p_networking() -> Result<(), Box> { println!(" Address {}: {}", i + 1, addr); } - let connect_result = codex_bindings::connect(&node, test_peer_id, &test_addresses).await; + let connect_result = storage_bindings::connect(&node, test_peer_id, &test_addresses).await; match connect_result { Ok(()) => println!("✓ Successfully connected to peer"), Err(e) => println!("✗ Failed to connect to peer: {}", e), @@ -75,7 +75,7 @@ async fn test_p2p_networking() -> Result<(), Box> { // Test getting peer information println!("\n=== Testing Peer Information ==="); - let peer_info_result = codex_bindings::get_peer_info(&node, test_peer_id).await; + let peer_info_result = storage_bindings::get_peer_info(&node, test_peer_id).await; match peer_info_result { Ok(peer_info) => { println!("✓ Successfully retrieved peer information:"); @@ -109,7 +109,7 @@ async fn test_p2p_networking() -> Result<(), Box> { for peer_id in test_peer_ids { println!("Testing peer ID: {}", peer_id); - let peer_info_result = codex_bindings::get_peer_info(&node, peer_id).await; + let peer_info_result = storage_bindings::get_peer_info(&node, peer_id).await; match peer_info_result { Ok(_) => println!(" ✓ Successfully retrieved peer info"), Err(_) => println!(" ✗ Failed to retrieve peer info (expected for test peer)"), @@ -121,13 +121,13 @@ async fn test_p2p_networking() -> Result<(), Box> { // Empty peer ID for connection println!("Testing connection with empty peer ID..."); - let empty_peer_result = codex_bindings::connect(&node, "", &test_addresses).await; + let empty_peer_result = storage_bindings::connect(&node, "", &test_addresses).await; assert!(empty_peer_result.is_err(), "Should fail with empty peer ID"); println!(" ✓ Correctly failed with empty peer ID"); // Empty addresses for connection println!("Testing connection with empty addresses..."); - let empty_addr_result = codex_bindings::connect(&node, test_peer_id, &[]).await; + let empty_addr_result = storage_bindings::connect(&node, test_peer_id, &[]).await; assert!( empty_addr_result.is_err(), "Should fail with empty addresses" @@ -136,15 +136,15 @@ async fn test_p2p_networking() -> Result<(), Box> { // Empty peer ID for peer info println!("Testing peer info with empty peer ID..."); - let empty_info_result = codex_bindings::get_peer_info(&node, "").await; + let empty_info_result = storage_bindings::get_peer_info(&node, "").await; assert!(empty_info_result.is_err(), "Should fail with empty peer ID"); println!(" ✓ Correctly failed with empty peer ID"); // Test concurrent P2P operations println!("\n=== Testing Concurrent P2P Operations ==="); - let peer_id_future1 = codex_bindings::get_peer_id(&node); - let peer_info_future1 = codex_bindings::get_peer_info(&node, "12D3KooWTestPeer1"); - let peer_info_future2 = codex_bindings::get_peer_info(&node, "12D3KooWTestPeer2"); + let peer_id_future1 = storage_bindings::get_peer_id(&node); + let peer_info_future1 = storage_bindings::get_peer_info(&node, "12D3KooWTestPeer1"); + let peer_info_future2 = storage_bindings::get_peer_info(&node, "12D3KooWTestPeer2"); let (peer_id_result, peer_info_result1, peer_info_result2) = tokio::join!(peer_id_future1, peer_info_future1, peer_info_future2); diff --git a/tests/storage_management.rs b/tests/storage_management.rs index d045864..17c4821 100644 --- a/tests/storage_management.rs +++ b/tests/storage_management.rs @@ -1,4 +1,4 @@ -//! Storage management integration test for the Codex Rust bindings +//! Storage management integration test for the Storage Rust bindings //! //! This test demonstrates how to manage storage operations: //! - List manifests @@ -7,9 +7,9 @@ //! - Delete content //! - Check content existence -use codex_bindings::{CodexConfig, CodexNode, LogLevel}; use std::fs::File; use std::io::Write; +use storage_bindings::{LogLevel, StorageConfig, StorageNode}; use tempfile::tempdir; #[tokio::test] @@ -17,7 +17,7 @@ async fn test_storage_management() -> Result<(), Box> { // Initialize logging let _ = env_logger::try_init(); - println!("Codex Rust Bindings - Storage Management Test"); + println!("Storage Rust Bindings - Storage Management Test"); println!("============================================="); // Create a temporary directory for our test @@ -31,23 +31,23 @@ async fn test_storage_management() -> Result<(), Box> { file.sync_all()?; println!("Test file created at: {}", file_path.display()); - // Create a minimal Codex configuration - println!("Creating Codex configuration..."); - let config = CodexConfig::new() + // Create a minimal Storage configuration + println!("Creating Storage configuration..."); + let config = StorageConfig::new() .log_level(LogLevel::Error) - .data_dir(temp_dir.path().join("codex_data")) + .data_dir(temp_dir.path().join("storage_data")) .block_retries(3000) .discovery_port(8097); - // Create and start a Codex node - println!("Creating and starting Codex node..."); - let mut node = CodexNode::new(config)?; + // Create and start a Storage node + println!("Creating and starting Storage node..."); + let mut node = StorageNode::new(config)?; node.start()?; println!("Node started successfully!"); // Get initial storage information println!("\n=== Initial Storage Information ==="); - let space_info = codex_bindings::space(&node).await?; + let space_info = storage_bindings::space(&node).await?; println!("Storage quota: {} bytes", space_info.quota_max_bytes); println!("Storage used: {} bytes", space_info.quota_used_bytes); println!( @@ -58,7 +58,7 @@ async fn test_storage_management() -> Result<(), Box> { // List initial manifests (should be empty) println!("\n=== Initial Manifests ==="); - let manifests = codex_bindings::manifests(&node).await?; + let manifests = storage_bindings::manifests(&node).await?; println!("Number of manifests: {}", manifests.len()); assert_eq!(manifests.len(), 0, "Should start with no manifests"); @@ -71,7 +71,7 @@ async fn test_storage_management() -> Result<(), Box> { // Upload a file to have some content println!("\n=== Uploading Test File ==="); - let upload_options = codex_bindings::UploadOptions::new() + let upload_options = storage_bindings::UploadOptions::new() .filepath(&file_path) .on_progress(|progress| { println!( @@ -81,26 +81,26 @@ async fn test_storage_management() -> Result<(), Box> { ); }); - let upload_result = codex_bindings::upload_file(&node, upload_options).await?; + let upload_result = storage_bindings::upload_file(&node, upload_options).await?; println!("File uploaded successfully!"); println!(" CID: {}", upload_result.cid); println!(" Size: {} bytes", upload_result.size); // Check if content exists println!("\n=== Checking Content Existence ==="); - let exists = codex_bindings::exists(&node, &upload_result.cid).await?; + let exists = storage_bindings::exists(&node, &upload_result.cid).await?; assert!(exists, "Uploaded content should exist"); println!("Content exists: {}", exists); // Check non-existent content (using a valid CID format that doesn't exist) let non_existent_cid = "bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"; - let non_existent = codex_bindings::exists(&node, non_existent_cid).await?; + let non_existent = storage_bindings::exists(&node, non_existent_cid).await?; assert!(!non_existent, "Non-existent content should not exist"); println!("Non-existent content exists: {}", non_existent); // Fetch manifest information println!("\n=== Fetching Manifest Information ==="); - let manifest = codex_bindings::fetch(&node, &upload_result.cid).await?; + let manifest = storage_bindings::fetch(&node, &upload_result.cid).await?; println!("Manifest CID: {}", manifest.cid); println!("Manifest size: {} bytes", manifest.dataset_size); println!("Manifest block size: {} bytes", manifest.block_size); @@ -110,7 +110,7 @@ async fn test_storage_management() -> Result<(), Box> { // List manifests after upload println!("\n=== Manifests After Upload ==="); - let manifests = codex_bindings::manifests(&node).await?; + let manifests = storage_bindings::manifests(&node).await?; println!("Number of manifests: {}", manifests.len()); assert_eq!(manifests.len(), 1, "Should have 1 manifest after upload"); @@ -123,7 +123,7 @@ async fn test_storage_management() -> Result<(), Box> { // Get updated storage information println!("\n=== Updated Storage Information ==="); - let space_info = codex_bindings::space(&node).await?; + let space_info = storage_bindings::space(&node).await?; println!("Storage quota: {} bytes", space_info.quota_max_bytes); println!("Storage used: {} bytes", space_info.quota_used_bytes); println!( @@ -139,16 +139,16 @@ async fn test_storage_management() -> Result<(), Box> { file2.write_all(b"This is a second test file for storage management.")?; file2.sync_all()?; - let upload_options2 = codex_bindings::UploadOptions::new().filepath(&file_path2); + let upload_options2 = storage_bindings::UploadOptions::new().filepath(&file_path2); - let upload_result2 = codex_bindings::upload_file(&node, upload_options2).await?; + let upload_result2 = storage_bindings::upload_file(&node, upload_options2).await?; println!("Second file uploaded successfully!"); println!(" CID: {}", upload_result2.cid); println!(" Size: {} bytes", upload_result2.size); // List manifests after second upload println!("\n=== Manifests After Second Upload ==="); - let manifests = codex_bindings::manifests(&node).await?; + let manifests = storage_bindings::manifests(&node).await?; println!("Number of manifests: {}", manifests.len()); assert_eq!( manifests.len(), @@ -165,18 +165,18 @@ async fn test_storage_management() -> Result<(), Box> { // Delete the first file println!("\n=== Deleting First File ==="); - codex_bindings::delete(&node, &upload_result.cid).await?; + storage_bindings::delete(&node, &upload_result.cid).await?; println!("First file deleted successfully!"); // Check if deleted content still exists println!("\n=== Checking Deleted Content ==="); - let exists_after_delete = codex_bindings::exists(&node, &upload_result.cid).await?; + let exists_after_delete = storage_bindings::exists(&node, &upload_result.cid).await?; assert!(!exists_after_delete, "Deleted content should not exist"); println!("Deleted content exists: {}", exists_after_delete); // List manifests after deletion println!("\n=== Manifests After Deletion ==="); - let manifests = codex_bindings::manifests(&node).await?; + let manifests = storage_bindings::manifests(&node).await?; println!("Number of manifests: {}", manifests.len()); assert_eq!(manifests.len(), 1, "Should have 1 manifest after deletion"); @@ -189,7 +189,7 @@ async fn test_storage_management() -> Result<(), Box> { // Get final storage information println!("\n=== Final Storage Information ==="); - let space_info = codex_bindings::space(&node).await?; + let space_info = storage_bindings::space(&node).await?; println!("Storage quota: {} bytes", space_info.quota_max_bytes); println!("Storage used: {} bytes", space_info.quota_used_bytes); println!( diff --git a/tests/thread_safe_tests.rs b/tests/thread_safe_tests.rs index d5a35d5..15ecebb 100644 --- a/tests/thread_safe_tests.rs +++ b/tests/thread_safe_tests.rs @@ -1,22 +1,22 @@ -use codex_bindings::{CodexConfig, CodexNode}; use std::sync::Arc; +use storage_bindings::{StorageConfig, StorageNode}; use tempfile::tempdir; #[tokio::test] async fn test_thread_safe_node_creation() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); + let config = StorageConfig::new().data_dir(temp_dir.path()); - let node = CodexNode::new(config).unwrap(); + let node = StorageNode::new(config).unwrap(); assert!(!node.is_started()); } #[tokio::test] async fn test_thread_safe_node_lifecycle() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); + let config = StorageConfig::new().data_dir(temp_dir.path()); - let mut node = CodexNode::new(config).unwrap(); + let mut node = StorageNode::new(config).unwrap(); node.start().unwrap(); assert!(node.is_started()); @@ -34,9 +34,9 @@ async fn test_thread_safe_node_lifecycle() { #[tokio::test] async fn test_node_cloning() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); + let config = StorageConfig::new().data_dir(temp_dir.path()); - let mut node1 = CodexNode::new(config).unwrap(); + let mut node1 = StorageNode::new(config).unwrap(); let node2 = node1.clone(); assert!(!node1.is_started()); @@ -53,9 +53,9 @@ async fn test_concurrent_access() { use tokio::task::JoinSet; let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); + let config = StorageConfig::new().data_dir(temp_dir.path()); - let node = Arc::new(CodexNode::new(config).unwrap()); + let node = Arc::new(StorageNode::new(config).unwrap()); node.start_async().await.unwrap(); let mut set = JoinSet::new(); @@ -79,21 +79,21 @@ fn test_send_sync_traits() { fn assert_sync() {} let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); - let _node = CodexNode::new(config).unwrap(); + let config = StorageConfig::new().data_dir(temp_dir.path()); + let _node = StorageNode::new(config).unwrap(); - assert_send::(); - assert_sync::(); + assert_send::(); + assert_sync::(); - assert_send::>(); + assert_send::>(); } #[test] fn test_clone_trait() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); + let config = StorageConfig::new().data_dir(temp_dir.path()); - let mut node1 = CodexNode::new(config).unwrap(); + let mut node1 = StorageNode::new(config).unwrap(); let node2 = node1.clone(); assert!(!node1.is_started()); @@ -107,8 +107,8 @@ fn test_clone_trait() { #[tokio::test] async fn test_send_between_threads() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); - let node = CodexNode::new(config).unwrap(); + let config = StorageConfig::new().data_dir(temp_dir.path()); + let node = StorageNode::new(config).unwrap(); let result = tokio::task::spawn(async move { let _version = node.version().unwrap(); @@ -122,17 +122,17 @@ async fn test_send_between_threads() { #[tokio::test] async fn test_async_file_upload() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); - let node = Arc::new(CodexNode::new(config).unwrap()); + let config = StorageConfig::new().data_dir(temp_dir.path()); + let node = Arc::new(StorageNode::new(config).unwrap()); node.start_async().await.unwrap(); let file_path = temp_dir.path().join("test.txt"); - std::fs::write(&file_path, b"Hello, Codex!").unwrap(); + std::fs::write(&file_path, b"Hello, Storage!").unwrap(); - let options = codex_bindings::UploadOptions::new().filepath(&file_path); + let options = storage_bindings::UploadOptions::new().filepath(&file_path); - let result = codex_bindings::upload_file(&node, options).await; + let result = storage_bindings::upload_file(&node, options).await; assert!(result.is_ok(), "Upload should succeed"); @@ -142,8 +142,8 @@ async fn test_async_file_upload() { #[tokio::test] async fn test_multiple_concurrent_operations() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); - let node = Arc::new(CodexNode::new(config).unwrap()); + let config = StorageConfig::new().data_dir(temp_dir.path()); + let node = Arc::new(StorageNode::new(config).unwrap()); node.start_async().await.unwrap(); @@ -177,14 +177,14 @@ async fn test_multiple_concurrent_operations() { #[tokio::test] async fn test_shared_node_across_tasks() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); + let config = StorageConfig::new().data_dir(temp_dir.path()); struct AppState { - node: Arc, + node: Arc, } let state = AppState { - node: Arc::new(CodexNode::new(config).unwrap()), + node: Arc::new(StorageNode::new(config).unwrap()), }; let mut handles = Vec::new(); @@ -203,7 +203,7 @@ async fn test_shared_node_across_tasks() { handles.push(tokio::task::spawn(async move { tokio::task::spawn_blocking(move || { - let mut node = CodexNode::new(CodexConfig::new()).unwrap(); + let mut node = StorageNode::new(StorageConfig::new()).unwrap(); node.start().unwrap(); node }) @@ -221,17 +221,17 @@ async fn test_shared_node_across_tasks() { #[tokio::test] async fn test_send_future_compatibility() { let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); - let node = Arc::new(CodexNode::new(config).unwrap()); + let config = StorageConfig::new().data_dir(temp_dir.path()); + let node = Arc::new(StorageNode::new(config).unwrap()); let future = async move { node.start_async().await.unwrap(); let file_path = temp_dir.path().join("test.txt"); - std::fs::write(&file_path, b"Hello, Codex!").unwrap(); + std::fs::write(&file_path, b"Hello, Storage!").unwrap(); - let options = codex_bindings::UploadOptions::new().filepath(&file_path); - let _result = codex_bindings::upload_file(&node, options).await.unwrap(); + let options = storage_bindings::UploadOptions::new().filepath(&file_path); + let _result = storage_bindings::upload_file(&node, options).await.unwrap(); "success" }; @@ -242,20 +242,20 @@ async fn test_send_future_compatibility() { #[tokio::test] async fn test_async_upload_download() { - use codex_bindings::{DownloadStreamOptions, UploadOptions}; + use storage_bindings::{DownloadStreamOptions, UploadOptions}; let temp_dir = tempdir().unwrap(); - let config = CodexConfig::new().data_dir(temp_dir.path()); - let node = Arc::new(CodexNode::new(config).unwrap()); + let config = StorageConfig::new().data_dir(temp_dir.path()); + let node = Arc::new(StorageNode::new(config).unwrap()); node.start_async().await.unwrap(); let file_path = temp_dir.path().join("test.txt"); - let test_content = b"Hello, Codex async API!"; + let test_content = b"Hello, Storage async API!"; std::fs::write(&file_path, test_content).unwrap(); let upload_options = UploadOptions::new().filepath(&file_path); - let upload_result = codex_bindings::upload_file(&node, upload_options) + let upload_result = storage_bindings::upload_file(&node, upload_options) .await .unwrap(); @@ -263,7 +263,7 @@ async fn test_async_upload_download() { let download_options = DownloadStreamOptions::new(&upload_result.cid).filepath(&download_path); let _download_result = - codex_bindings::download_stream(&node, &upload_result.cid, download_options) + storage_bindings::download_stream(&node, &upload_result.cid, download_options) .await .unwrap(); diff --git a/tests/two_node_network.rs b/tests/two_node_network.rs index 9f53e3c..65f1c1c 100644 --- a/tests/two_node_network.rs +++ b/tests/two_node_network.rs @@ -1,17 +1,17 @@ -//! Two-node networking integration test for the Codex Rust bindings +//! Two-node networking integration test for the Storage Rust bindings //! -//! This test demonstrates how to create and connect two Codex nodes: +//! This test demonstrates how to create and connect two Storage nodes: //! - Create two separate nodes //! - Configure them to discover each other //! - Connect the nodes //! - Transfer data between nodes -use codex_bindings::{ - connect, download_stream, upload_file, CodexConfig, CodexNode, DownloadStreamOptions, LogLevel, - UploadOptions, -}; use std::fs::File; use std::io::Write; +use storage_bindings::{ + connect, download_stream, upload_file, DownloadStreamOptions, LogLevel, StorageConfig, + StorageNode, UploadOptions, +}; use tempfile::tempdir; #[tokio::test] @@ -19,7 +19,7 @@ async fn test_two_node_network() -> Result<(), Box> { // Initialize logging let _ = env_logger::try_init(); - println!("Codex Rust Bindings - Two-Node Network Test"); + println!("Storage Rust Bindings - Two-Node Network Test"); println!("============================================"); // Create temporary directories for our test @@ -43,7 +43,7 @@ async fn test_two_node_network() -> Result<(), Box> { // Configure node1 to listen on a specific port println!("\n=== Creating Node 1 ==="); - let node1_config = CodexConfig::new() + let node1_config = StorageConfig::new() .log_level(LogLevel::Info) .data_dir(&node1_dir) .storage_quota(100 * 1024 * 1024) // 100 MB @@ -54,12 +54,12 @@ async fn test_two_node_network() -> Result<(), Box> { "/ip4/0.0.0.0/tcp/0".to_string(), ]); - let mut node1 = CodexNode::new(node1_config)?; + let mut node1 = StorageNode::new(node1_config)?; node1.start()?; let node1_peer_id = node1.peer_id()?; let node1_repo = node1.repo()?; - let debug1 = codex_bindings::debug(&node1).await?; + let debug1 = storage_bindings::debug(&node1).await?; println!("Node 1 started:"); println!(" Peer ID: {}", node1_peer_id); @@ -68,7 +68,7 @@ async fn test_two_node_network() -> Result<(), Box> { // Configure node2 with different ports and bootstrap to node1 println!("\n=== Creating Node 2 ==="); - let mut node2_config = CodexConfig::new() + let mut node2_config = StorageConfig::new() .log_level(LogLevel::Info) .data_dir(&node2_dir) .storage_quota(100 * 1024 * 1024) // 100 MB @@ -82,7 +82,7 @@ async fn test_two_node_network() -> Result<(), Box> { "/ip4/0.0.0.0/tcp/0".to_string(), ]; - let mut node2 = CodexNode::new(node2_config)?; + let mut node2 = StorageNode::new(node2_config)?; node2.start()?; let node2_peer_id = node2.peer_id()?; @@ -94,7 +94,7 @@ async fn test_two_node_network() -> Result<(), Box> { // Get debug information for both nodes println!("\n=== Node Debug Information ==="); - let debug2 = codex_bindings::debug(&node2).await?; + let debug2 = storage_bindings::debug(&node2).await?; println!("Node 1 debug info:"); println!(" Peer ID: {}", debug1.peer_id()); @@ -122,7 +122,7 @@ async fn test_two_node_network() -> Result<(), Box> { let mut connection_successful = false; for addr in &node1_addresses { - match connect(&node2, &node1_peer_id, &[addr.clone()]).await { + match connect(&node2, &node1_peer_id, std::slice::from_ref(addr)).await { Ok(()) => { println!("✓ Successfully connected node2 to node1 at {}", addr); connection_successful = true; @@ -157,7 +157,7 @@ async fn test_two_node_network() -> Result<(), Box> { // Check if the content exists on node1 println!("\n=== Checking Content on Node 1 ==="); - let exists_on_node1 = codex_bindings::exists(&node1, &upload_result.cid).await?; + let exists_on_node1 = storage_bindings::exists(&node1, &upload_result.cid).await?; assert!(exists_on_node1, "Content should exist on node1"); println!("Content exists on node1: {}", exists_on_node1); @@ -168,7 +168,7 @@ async fn test_two_node_network() -> Result<(), Box> { let fetch_timeout = tokio::time::Duration::from_secs(30); let fetch_result = tokio::time::timeout( fetch_timeout, - codex_bindings::fetch(&node2, &upload_result.cid), + storage_bindings::fetch(&node2, &upload_result.cid), ) .await; @@ -192,7 +192,7 @@ async fn test_two_node_network() -> Result<(), Box> { } // Check if content exists on node2 after fetch attempt - let exists_on_node2 = codex_bindings::exists(&node2, &upload_result.cid).await?; + let exists_on_node2 = storage_bindings::exists(&node2, &upload_result.cid).await?; println!("Content exists on node2: {}", exists_on_node2); // Download the file from node2 (if it has the content) @@ -230,8 +230,8 @@ async fn test_two_node_network() -> Result<(), Box> { // Get final debug information println!("\n=== Final Node Status ==="); - let final_debug1 = codex_bindings::debug(&node1).await?; - let final_debug2 = codex_bindings::debug(&node2).await?; + let final_debug1 = storage_bindings::debug(&node1).await?; + let final_debug2 = storage_bindings::debug(&node2).await?; println!("Node 1 final status:"); println!(" Peer ID: {}", final_debug1.peer_id()); @@ -253,8 +253,8 @@ async fn test_two_node_network() -> Result<(), Box> { // Get storage information println!("\n=== Storage Information ==="); - let space1 = codex_bindings::space(&node1).await?; - let space2 = codex_bindings::space(&node2).await?; + let space1 = storage_bindings::space(&node1).await?; + let space2 = storage_bindings::space(&node2).await?; println!("Node 1 storage:"); println!(" Used: {} bytes", space1.quota_used_bytes); @@ -274,8 +274,8 @@ async fn test_two_node_network() -> Result<(), Box> { // List manifests on both nodes println!("\n=== Manifests ==="); - let manifests1 = codex_bindings::manifests(&node1).await?; - let manifests2 = codex_bindings::manifests(&node2).await?; + let manifests1 = storage_bindings::manifests(&node1).await?; + let manifests2 = storage_bindings::manifests(&node2).await?; println!("Node 1 manifests: {}", manifests1.len()); for manifest in &manifests1 { diff --git a/vendor/nim-codex b/vendor/nim-codex deleted file mode 160000 index bd36032..0000000 --- a/vendor/nim-codex +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bd360322515addd487c8f97fc4447bf25eda6c99