pub trait WritableDataSet<E>: DataSet<E>{
// 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_jsonserialization - 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;Conflictif the id is held outside the set; a new entity must fit the setreplace: replaces or creates;Conflictif the entity would leave the set or the id is held outside it (where verifiable)patch:NotFoundwhen the entity is missing or outside the set;Conflictwhen 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§
Sourcefn 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 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.
Sourcefn 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 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.
Sourcefn 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,
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§
Sourcefn 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,
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".