Skip to main content
The StateAdapter trait defines the interface for state persistence in III. State adapters allow you to store, retrieve, and manipulate key-value data across different storage backends.

Trait Definition

#[async_trait]
pub trait StateAdapter: Send + Sync {
    async fn set(&self, scope: &str, key: &str, value: Value) -> anyhow::Result<SetResult>;
    async fn get(&self, scope: &str, key: &str) -> anyhow::Result<Option<Value>>;
    async fn delete(&self, scope: &str, key: &str) -> anyhow::Result<()>;
    async fn update(
        &self,
        scope: &str,
        key: &str,
        ops: Vec<UpdateOp>,
    ) -> anyhow::Result<UpdateResult>;
    async fn list(&self, scope: &str) -> anyhow::Result<Vec<Value>>;
    async fn list_groups(&self) -> anyhow::Result<Vec<String>>;
    async fn destroy(&self) -> anyhow::Result<()>;
}
Source: /workspace/source/src/modules/state/adapters/mod.rs:16

Methods

set

async fn set(&self, scope: &str, key: &str, value: Value) -> anyhow::Result<SetResult>
Stores a value in the state store. Parameters:
  • scope - The namespace or group for the key
  • key - The unique identifier within the scope
  • value - JSON value to store
Returns: SetResult containing information about the operation

get

async fn get(&self, scope: &str, key: &str) -> anyhow::Result<Option<Value>>
Retrieves a value from the state store. Parameters:
  • scope - The namespace or group for the key
  • key - The unique identifier within the scope
Returns: Some(Value) if found, None if not found

delete

async fn delete(&self, scope: &str, key: &str) -> anyhow::Result<()>
Removes a key-value pair from the state store. Parameters:
  • scope - The namespace or group for the key
  • key - The unique identifier within the scope

update

async fn update(
    &self,
    scope: &str,
    key: &str,
    ops: Vec<UpdateOp>,
) -> anyhow::Result<UpdateResult>
Performs atomic update operations on a stored value. Parameters:
  • scope - The namespace or group for the key
  • key - The unique identifier within the scope
  • ops - Vector of update operations to apply
Returns: UpdateResult containing the updated value

list

async fn list(&self, scope: &str) -> anyhow::Result<Vec<Value>>
Lists all values within a scope. Parameters:
  • scope - The namespace or group to list
Returns: Vector of all values in the scope

list_groups

async fn list_groups(&self) -> anyhow::Result<Vec<String>>
Lists all available scopes/groups in the state store. Returns: Vector of scope names

destroy

async fn destroy(&self) -> anyhow::Result<()>
Cleans up resources and closes connections for the adapter.

UpdateOp

The UpdateOp enum defines atomic operations that can be applied to stored values:
pub enum UpdateOp {
    Set { path: FieldPath, value: Option<Value> },
    Merge { path: Option<FieldPath>, value: Value },
    Increment { path: FieldPath, by: i64 },
    Decrement { path: FieldPath, by: i64 },
    Remove { path: FieldPath },
}

Update Operations

  • Set: Sets a field at the specified path to a new value
  • Merge: Merges an object with the existing value
  • Increment: Adds a numeric value to a field
  • Decrement: Subtracts a numeric value from a field
  • Remove: Removes a field at the specified path

Available Adapters

RedisAdapter

Redis-based state persistence with atomic operations.
modules: {
  state: {
    adapter: "modules::state::RedisAdapter",
    config: {
      redis_url: "redis://localhost:6379"
    }
  }
}
Source: /workspace/source/src/modules/state/adapters/redis_adapter.rs

KvStore

In-memory key-value store for development and testing.
modules: {
  state: {
    adapter: "modules::state::KvStore"
  }
}
Source: /workspace/source/src/modules/state/adapters/kv_store.rs

Bridge

Bridges state operations to another III instance.
modules: {
  state: {
    adapter: "modules::state::Bridge",
    config: {
      url: "https://remote-instance.example.com"
    }
  }
}
Source: /workspace/source/src/modules/state/adapters/bridge.rs

Example Implementation

use async_trait::async_trait;
use serde_json::Value;
use iii_sdk::{UpdateOp, UpdateResult, types::SetResult};

struct CustomStateAdapter {
    // Your storage implementation
}

#[async_trait]
impl StateAdapter for CustomStateAdapter {
    async fn set(&self, scope: &str, key: &str, value: Value) -> anyhow::Result<SetResult> {
        // Store the value
        Ok(SetResult {
            key: key.to_string(),
            value: Some(value),
            created: true,
        })
    }

    async fn get(&self, scope: &str, key: &str) -> anyhow::Result<Option<Value>> {
        // Retrieve the value
        Ok(None)
    }

    async fn delete(&self, scope: &str, key: &str) -> anyhow::Result<()> {
        // Delete the key
        Ok(())
    }

    async fn update(
        &self,
        scope: &str,
        key: &str,
        ops: Vec<UpdateOp>,
    ) -> anyhow::Result<UpdateResult> {
        // Apply update operations atomically
        for op in &ops {
            match op {
                UpdateOp::Set { path, value } => { /* ... */ },
                UpdateOp::Merge { path, value } => { /* ... */ },
                UpdateOp::Increment { path, by } => { /* ... */ },
                UpdateOp::Decrement { path, by } => { /* ... */ },
                UpdateOp::Remove { path } => { /* ... */ },
            }
        }
        Ok(UpdateResult {
            key: key.to_string(),
            value: None,
        })
    }

    async fn list(&self, scope: &str) -> anyhow::Result<Vec<Value>> {
        Ok(vec![])
    }

    async fn list_groups(&self) -> anyhow::Result<Vec<String>> {
        Ok(vec![])
    }

    async fn destroy(&self) -> anyhow::Result<()> {
        Ok(())
    }
}

Build docs developers (and LLMs) love