Skip to main content

WritableDataSet

Trait WritableDataSet 

Source
pub trait WritableDataSet<E>: DataSet<E>
where E: Entity<Self::Value>,
{ // Required methods fn insert<'life0, 'life1, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, entity: &'life1 E, ) -> Pin<Box<dyn Future<Output = Result<E, VantageError>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait; fn replace<'life0, 'life1, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, entity: &'life1 E, ) -> Pin<Box<dyn Future<Output = Result<E, VantageError>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait; fn patch<'life0, 'life1, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, partial: &'life1 E, ) -> Pin<Box<dyn Future<Output = Result<E, VantageError>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait; // Provided method fn patch_changed<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, before: &'life1 E, after: &'life2 E, ) -> Pin<Box<dyn Future<Output = Result<(), VantageError>> + Send + 'async_trait>> where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: Sized + WritableValueSet + Sync + 'async_trait, E: TryIntoRecord<Self::Value>, <E as TryIntoRecord<Self::Value>>::Error: Debug, Self::Value: PartialEq + Clone { ... } }
Expand description

Write operations on typed entities with automatic serialization.

This trait provides convenient write operations that automatically handle entity serialization to the storage format. All operations follow idempotent patterns safe for retry in distributed systems.

§Serialization Behavior

Entities are automatically serialized to the storage’s Value type before persistence. The serialization format depends on your storage backend:

  • JSON databases use serde_json serialization
  • Binary stores may use CBOR or custom formats
  • Document databases preserve nested structure

§Idempotency Guarantees

Every write is retry-safe, and a write through a narrowed set only touches rows inside that set — the contract is the one on WritableValueSet:

  • insert: returns the stored entity untouched if the id is in the set; Conflict if the id is held outside the set; a new entity must fit the set
  • replace: replaces or creates; Conflict if the entity would leave the set or the id is held outside it (where verifiable)
  • patch: NotFound when the entity is missing or outside the set; Conflict when the patched entity would leave the set

§Example

ⓘ
use vantage_dataset::dataset::WritableDataSet;

let user = User {
    name: "Alice".to_string(),
    email: "alice@example.com".to_string(),
    age: 30,
};

// Idempotent insert
users.insert("user-123", user.clone()).await?;

// Update specific fields
let mut updated_user = user;
updated_user.age = 31;
users.replace("user-123", updated_user).await?;

Required Methods§

Source

fn insert<'life0, 'life1, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, entity: &'life1 E, ) -> Pin<Box<dyn Future<Output = Result<E, VantageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Insert entity with a specific ID (often generated) (HTTP POST with ID)

Idempotent: an id already in the set returns the stored entity untouched; an id held outside the set is Conflict; a new entity must fit the set.

Returns: Entity as it was stored.

§Use Case

Generate unique ID and store centity while avoiding duplicates.

Source

fn replace<'life0, 'life1, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, entity: &'life1 E, ) -> Pin<Box<dyn Future<Output = Result<E, VantageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Replace the entire entity at the specified ID (HTTP PUT)

Idempotent: completely overwrites existing data if present. If possible, will remove/recreate entity; therefore if entity doesn’t contain certain attributes which were present in the database, those will be removed. If entity does not exist, will create it.

Confined: an entity in the set is replaced (Conflict if the new entity would leave the set); an id held outside the set is Conflict where the backend can verify it; a created entity must fit the set.

Returns: entity as it was stored.

§Use Case

Replace with a new version of a entity.

Source

fn patch<'life0, 'life1, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, partial: &'life1 E, ) -> Pin<Box<dyn Future<Output = Result<E, VantageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, Self: 'async_trait,

Partially update an entity by merging with the provided data (HTTP PATCH)

NotFound (marked) when the entity is missing or outside the set; Conflict when the patched entity would leave the set. The exact merge behavior depends on the storage implementation - typically merges object fields for JSON-like values.

Returns: entity as it was stored (not only the partial change).

§Use Case

Update only the modified fields of a entity.

Provided Methods§

Source

fn patch_changed<'life0, 'life1, 'life2, 'async_trait>( &'life0 self, id: impl Into<Self::Id> + Send + 'async_trait, before: &'life1 E, after: &'life2 E, ) -> Pin<Box<dyn Future<Output = Result<(), VantageError>> + Send + 'async_trait>>
where 'life0: 'async_trait, 'life1: 'async_trait, 'life2: 'async_trait, Self: Sized + WritableValueSet + Sync + 'async_trait, E: TryIntoRecord<Self::Value>, <E as TryIntoRecord<Self::Value>>::Error: Debug, Self::Value: PartialEq + Clone,

Write only the fields that differ between before and after.

The read-modify-write most business logic wants. Read the entity, change the fields the operation owns, and call this with both — only the changed fields reach storage.

Prefer this over replace whenever the entity came from a read. replace writes every field the caller read, so it silently reverts a field another writer changed while the caller was working, and it saves back values the caller never looked at — including any the entity failed to parse and filled with a default.

Deriving the change set from two entity values means no caller writes out field names by hand; the struct stays the one place they are declared.

Writes nothing and returns Ok(()) when the two are identical.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementors§