dynarust_client 0.1.0

An official async Rust client for the DynaRust distributed key-value store.
Documentation

🦀 DynaRust Client (Official Rust SDK)

The official, asynchronous, and type-safe Rust client for DynaRust — the distributed, horizontally scalable key-value store.

This library provides a seamless wrapper around the DynaRust REST API, allowing you to interact with your cluster using strongly-typed Rust structs, automatic JSON deserialization, and async/await syntax 🔄.


✨ Key Features

  • 🚀 Async by Default: Built on top of tokio and reqwest for non-blocking, high-performance I/O.
  • 📦 Strongly Typed: Uses standard serde traits. Fetch data from DynaRust and deserialize it directly into your own custom Rust structs!
  • 🔒 Built-in Authentication: Easily manage and attach JWT tokens for secure PUT and DELETE operations.
  • 🛡️ Error Handling: Meaningful, standardized DynaError types (e.g., NotFound, Unauthorized) so you don't have to parse raw HTTP status codes.

📦 Installation

Add the client to your project using Cargo:

cargo add dynarust_client

Basic usage

use dynarust_client::structs::{DynaClient, DynaError};
use serde::{Deserialize, Serialize};

// 1️⃣ Define your custom data structure
#[derive(Debug, Serialize, Deserialize, Clone)]
struct UserProfile {
    username: String,
    level: u32,
    is_active: bool,
}

#[tokio::main]
async fn main() -> Result<(), DynaError> {
    // 2️⃣ Initialize the client pointing to any node in your cluster
    let mut client = DynaClient::new("http://localhost:6660");

    // 3️⃣ Authenticate (Registers if new, logs in if exists. Token is saved in client)
    client.auth("player_1", "super_secret_password").await?;
    println!("✅ Authenticated successfully!");

    // 4️⃣ Write Data (PUT)
    let profile = UserProfile {
        username: "player_1".to_string(),
        level: 42,
        is_active: true,
    };
    
    let saved_record = client.put_value("users", "profile_data", &profile).await?;
    println!("📝 Data saved! Version: {}", saved_record.version);

    // 5️⃣ Read Data (GET)
    // We map the incoming JSON directly back into our UserProfile struct
    let fetched = client.get_value::<UserProfile>("users", "profile_data").await?;
    println!("🔍 Fetched level: {}", fetched.value.level);

    // 6️⃣ Delete Data (DELETE)
    client.delete_value("users", "profile_data").await?;
    println!("🗑️ Data deleted!");

    Ok(())
}

Real-Time Subscriptions (SSE)

use futures_util::StreamExt; // Required for stream iteration

#[tokio::main]
async fn main() {
    let client = DynaClient::new("http://localhost:6660");

    // Subscribe to a key. Returns an async Stream of your typed structs!
    let mut stream = client.subscribe::<UserProfile>("users", "profile_data").await.unwrap();

    println!("🎧 Listening for live updates...");

    // Process updates in real-time as they happen across the cluster
    while let Some(update) = stream.next().await {
        match update {
            Ok(record) => println!("🔥 LIVE UPDATE: {} leveled up to {}!", record.value.username, record.value.level),
            Err(e) => eprintln!("Stream error: {}", e),
        }
    }
}

📡 API Reference DynaClient::new(base_url: &str)

Creates a new client instance. You can point this to any active node in your DynaRust cluster.

client.auth(user: &str, secret: &str)

Registers or logs in a user. If successful, automatically securely stores the returned JWT token inside the DynaClient instance for future PUT and DELETE requests.

client.set_token(token: String)

Manually attach a JWT authentication token to the client (useful if you handle authentication elsewhere).

client.get_value::(table: &str, key: &str)

Retrieves the latest version of a key from the specified table. Does not require authentication.

Returns: Result<VersionedValue, DynaError>

client.put_value(table: &str, key: &str, value: &T)

Creates or updates a key. Requires the client to be authenticated as the record's owner.

Returns: Result<VersionedValue, DynaError>

client.delete_value(table: &str, key: &str)

Deletes a key from the table. Requires the client to be authenticated as the record's owner.

Returns: Result<(), DynaError>

client.subscribe::(table: &str, key: &str)

Opens a Server-Sent Events (SSE) connection to the cluster.

Returns: An asynchronous Stream that yields Result<VersionedValue, DynaError> whenever the key is modified.

🆘 Troubleshooting Error: Request failed: error sending request... connection refused Make sure your DynaRust node is running and accessible at the URL provided to DynaClient::new(). If running in Docker, ensure your ports are mapped correctly.

Error: Unexpected status 401: Unauthorized access You attempted a write/delete operation without authenticating. Make sure to call client.auth() or client.set_token() before making the request.

Error: trait bound is not satisfied Ensure the struct you are passing to generic methods has #[derive(Serialize, Deserialize)] attached to it.

🤝 Contributing

Pull requests are welcome! If you find a bug or want to help expand this client, feel free to open an issue or submit a PR on the main repository.


Next Step

To make the SSE (subscribe) and other missing functions actually work in your Rust code, we'll need to update src/structs.rs and add reqwest-eventsource and futures-util to your Cargo.toml.

Would you like me to write out the updated src/structs.rs file containing the auth, put_value, delete_value, and subscribe implementations?