Skip to main content

vantage_dataset/traits/
dataset.rs

1use crate::{ActiveEntity, traits::ValueSet};
2
3use super::Result;
4use async_trait::async_trait;
5use indexmap::IndexMap;
6use vantage_types::Entity;
7
8/// Entity-aware dataset operations built on top of the [`ValueSet`] foundation.
9///
10/// `DataSet` bridges the gap between raw storage values and typed Rust entities,
11/// providing automatic serialization/deserialization while preserving the flexibility
12/// of the underlying storage backend.
13///
14/// # Type Parameters
15///
16/// - `E`: The entity type that implements [`Entity`] trait, typically your domain models
17///
18/// # Relationship to ValueSet
19///
20/// While [`ValueSet`] works with raw storage values (JSON, CBOR, etc.), `DataSet`
21/// adds a typed layer that handles conversion between entities and storage format:
22///
23/// ```text
24/// Entity <--serde--> Value <--storage--> Backend
25/// ```
26///
27/// This separation allows the same storage backend to efficiently support both
28/// raw value operations and typed entity operations as needed.
29///
30/// # Implementation Strategy
31///
32/// Implement the specific capability traits your data source supports:
33/// - [`ReadableDataSet`] for read-only sources (CSV files, APIs)
34/// - [`InsertableDataSet`] for append-only sources (message queues, logs)
35/// - [`WritableDataSet`] for full CRUD sources (databases, caches)
36/// - `entityDataSet` for change-tracking scenarios (interactive applications)
37///
38/// # Example
39///
40/// ```rust,ignore
41/// use vantage_dataset::dataset::{DataSet, ReadableDataSet, WritableDataSet};
42/// use serde::{Deserialize, Serialize};
43///
44/// #[derive(Serialize, Deserialize, Clone)]
45/// struct User {
46///     name: String,
47///     email: String,
48///     age: u32,
49/// }
50///
51/// // Your storage implementation
52/// struct UserTable;
53///
54/// impl ValueSet for UserTable {
55///     type Id = String;
56///     type Value = serde_json::Value;
57/// }
58///
59/// impl DataSet<User> for UserTable {}
60///
61/// impl ReadableDataSet<User> for UserTable {
62///     async fn list(&self) -> Result<IndexMap<String, User>> {
63///         // Implementation converts storage values to entities
64///     }
65/// }
66/// ```
67#[async_trait]
68pub trait DataSet<E>: ValueSet
69where
70    E: Entity<Self::Value>,
71{
72}
73
74/// Read-only access to typed entities with automatic deserialization.
75///
76/// This trait provides convenient access to entities without requiring knowledge
77/// of the underlying storage format. The implementation handles conversion from
78/// raw storage values to typed entities automatically.
79///
80/// # Performance Considerations
81///
82/// Entity deserialization has overhead compared to raw value access. For
83/// performance-critical scenarios, consider using [`crate::ReadableValueSet`] directly
84/// and handling deserialization manually or in batches.
85///
86/// # Example
87///
88/// ```rust,ignore
89/// use vantage_dataset::dataset::ReadableDataSet;
90///
91/// // Type-safe entity access
92/// let all_users: IndexMap<String, User> = users.list().await?;
93/// let specific_user: User = users.get(&user_id).await?;
94///
95/// // Sample data without loading everything
96/// if let Some((id, user)) = users.get_some().await? {
97///     println!("Found user: {} with ID {}", user.name, id);
98/// }
99/// ```
100#[async_trait]
101pub trait ReadableDataSet<E>: DataSet<E>
102where
103    E: Entity<Self::Value>,
104{
105    /// Retrieve all entities with their IDs.
106    ///
107    /// Returns an ordered map preserving insertion order where supported by the backend.
108    /// Each storage value is automatically deserialized to the entity type.
109    ///
110    /// # Performance Warning
111    ///
112    /// This method loads all data into memory. Use with caution for large datasets.
113    /// Consider implementing pagination for production use.
114    async fn list(&self) -> Result<IndexMap<Self::Id, E>>;
115
116    /// Retrieve a specific entity by ID.
117    ///
118    /// The storage value is automatically deserialized to the entity type.
119    ///
120    /// # Errors
121    ///
122    /// Returns an error if the entity doesn't exist or deserialization fails.
123    async fn get(&self, id: impl Into<Self::Id> + Send) -> Result<E>;
124
125    /// Retrieve one single entity from the set. If entities are ordered - return first entity.
126    ///
127    /// Useful for sampling data or checking if the dataset contains any entities.
128    /// Returns `None` if the dataset is empty.
129    async fn get_some(&self) -> Result<Option<(Self::Id, E)>>;
130}
131
132/// Write operations on typed entities with automatic serialization.
133///
134/// This trait provides convenient write operations that automatically handle
135/// entity serialization to the storage format. All operations follow idempotent
136/// patterns safe for retry in distributed systems.
137///
138/// # Serialization Behavior
139///
140/// Entities are automatically serialized to the storage's `Value` type before
141/// persistence. The serialization format depends on your storage backend:
142/// - JSON databases use `serde_json` serialization
143/// - Binary stores may use CBOR or custom formats
144/// - Document databases preserve nested structure
145///
146/// # Idempotency Guarantees
147///
148/// All write operations are designed to be safely retryable:
149/// - `insert`: No-op if ID already exists
150/// - `replace`: Always succeeds, overwrites existing data
151/// - `patch`: Atomic update, fails if entity doesn't exist
152///
153/// # Example
154///
155/// ```rust,ignore
156/// use vantage_dataset::dataset::WritableDataSet;
157///
158/// let user = User {
159///     name: "Alice".to_string(),
160///     email: "alice@example.com".to_string(),
161///     age: 30,
162/// };
163///
164/// // Idempotent insert
165/// users.insert(&"user-123".to_string(), user.clone()).await?;
166///
167/// // Update specific fields
168/// let mut updated_user = user;
169/// updated_user.age = 31;
170/// users.replace(&"user-123".to_string(), updated_user).await?;
171/// ```
172#[async_trait]
173pub trait WritableDataSet<E>: DataSet<E>
174where
175    E: Entity<Self::Value>,
176{
177    /// Insert entity with a specific ID (often generated) (HTTP POST with ID)
178    ///
179    /// **Idempotent**: Succeeds if no entity exists with the given ID. If
180    /// entity already exists, must return success without overwriting
181    /// data, returning original data.
182    ///
183    /// **Returns**: Entity as it was stored.
184    ///
185    /// # Use Case
186    /// Generate unique ID and store centity while avoiding duplicates.
187    async fn insert(&self, id: &Self::Id, entity: &E) -> Result<E>;
188
189    /// Replace the entire entity at the specified ID (HTTP PUT)
190    ///
191    /// **Idempotent**: Always succeeds, completely overwrites existing data
192    /// if present. If possible, will remove/recreate entity; therefore if
193    /// `entity` doesn't contain certain attributes which were present in the
194    /// database, those will be removed. If entity does not exist, will
195    /// create it.
196    ///
197    /// **Returns**: entity as it was stored.
198    ///
199    /// # Use Case
200    /// Replace with a new version of a entity.
201    async fn replace(&self, id: &Self::Id, entity: &E) -> Result<E>;
202
203    /// Partially update an entity by merging with the provided data (HTTP PATCH)
204    ///
205    /// **Fails if entity doesn't exist**. The exact merge behavior depends on
206    /// the storage implementation - typically merges object fields for JSON-like values.
207    ///
208    /// **Returns**: entity as it was stored (not only the partial change).
209    ///
210    /// # Use Case
211    /// Update only the modified fields of a entity.
212    async fn patch(&self, id: &Self::Id, partial: &E) -> Result<E>;
213
214    /// Delete a entity by ID (HTTP DELETE)
215    ///
216    /// **Idempotent**: Always succeeds, even if the entity doesn't exist.
217    /// This allows safe cleanup operations without checking existence first.
218    async fn delete(&self, id: &Self::Id) -> Result<()>;
219
220    /// Delete all entities in the set (HTTP DELETE without ID)
221    ///
222    /// **Idempotent**: All entities in the set will be deleted.
223    /// Executing several times is OK.
224    ///
225    /// Execute on a subset of your entire database.
226    async fn delete_all(&self) -> Result<()>;
227}
228
229/// Append-only operations with automatic ID generation.
230///
231/// This trait is designed for storage backends that naturally generate unique IDs
232/// for new entities, such as message queues, event streams, or auto-incrementing
233/// database tables.
234///
235/// # Idempotency Considerations
236///
237/// Unlike other dataset operations, `insert_return_id` is **not idempotent** because
238/// each call generates a new ID. Use this pattern only when:
239/// - Your system can handle duplicate entities (event sourcing)
240/// - You have application-level deduplication
241/// - The storage naturally handles uniqueness (like message queues)
242///
243/// For idempotent operations, prefer [`WritableDataSet::insert`] with predetermined IDs.
244///
245/// # Example
246///
247/// ```rust,ignore
248/// use vantage_dataset::dataset::InsertableDataSet;
249///
250/// // Message queue scenario - each event gets unique ID
251/// let event = UserLoginEvent {
252///     user_id: "user-123".to_string(),
253///     timestamp: Utc::now(),
254///     ip_address: "192.168.1.1".to_string(),
255/// };
256///
257/// let event_id = events.insert_return_id(event).await?;
258/// println!("Generated event ID: {}", event_id);
259/// ```
260#[async_trait]
261pub trait InsertableDataSet<E>: DataSet<E>
262where
263    E: Entity<Self::Value>,
264{
265    /// Insert an entity and return the generated ID.
266    ///
267    /// The storage backend generates a unique identifier for the new entity.
268    /// The entity is automatically serialized to the storage format.
269    ///
270    /// # Warning
271    ///
272    /// This method is **not idempotent** - each call creates a new entity with
273    /// a new ID, even if the entity data is identical.
274    async fn insert_return_id(&self, entity: &E) -> Result<Self::Id>;
275}
276
277/// Change tracking for typed entities with automatic persistence.
278///
279/// This trait extends readable and writable datasets with a "entity" pattern that
280/// tracks entity modifications and enables deferred persistence. entities act as
281/// smart wrappers around entities that know how to save themselves back to storage.
282///
283/// # entity Pattern Benefits
284///
285/// - **Change tracking**: Only modified fields are serialized and persisted
286/// - **Type safety**: Work with native Rust entities, not raw values
287/// - **Optimistic locking**: Conflict detection in concurrent scenarios
288/// - **Deferred persistence**: Batch multiple changes before saving
289/// - **Interactive editing**: Perfect for UI scenarios with undo/redo
290///
291/// # Example
292///
293/// ```rust,ignore
294/// use vantage_dataset::dataset::entityDataSet;
295///
296/// // Get entity for interactive editing
297/// let mut user_entity = users.get_entity(&user_id).await?.unwrap();
298///
299/// // Modify through standard field access
300/// user_entity.name = "Alice Smith".to_string();
301/// user_entity.age = 31;
302///
303/// // Changes are automatically tracked and persisted
304/// user_entity.save().await?;
305///
306/// // Or work with multiple entities
307/// let mut entities = users.list_entities().await?;
308/// for mut entity in entities {
309///     entity.status = Status::Processed;
310///     entity.save().await?; // Each saves independently
311/// }
312/// ```
313#[async_trait]
314pub trait ActiveEntitySet<E>: ReadableDataSet<E> + WritableDataSet<E>
315where
316    E: Entity<Self::Value>
317        + vantage_types::IntoRecord<Self::Value>
318        + vantage_types::TryFromRecord<Self::Value>
319        + Send
320        + Sync
321        + Clone,
322{
323    /// Retrieve an entity wrapped for change tracking and deferred persistence.
324    ///
325    /// The returned `entity` can be modified in-place and will track all
326    /// changes for efficient persistence when `save()` is called.
327    ///
328    /// # Example
329    ///
330    /// ```rust,ignore
331    /// let mut user = table.get_entity(&"user123".to_string()).await?
332    ///     .unwrap_or_else(|| table.new_entity("user123".to_string(), User {
333    ///         id: Some("user123".to_string()),
334    ///         name: "Default User".to_string(),
335    ///         active: false,
336    ///     }));
337    ///
338    /// user.active = true;
339    /// user.save().await?;
340    /// ```
341    ///
342    /// # Returns
343    ///
344    /// - `Ok(Some(entity))`: Entity wrapper with change tracking
345    /// - `Ok(None)`: entity doesn't exist
346    /// - `Err`: Storage or deserialization error
347    async fn get_entity(&self, id: &Self::Id) -> Result<Option<ActiveEntity<'_, Self, E>>> {
348        match self.get(id.clone()).await {
349            Ok(data) => Ok(Some(ActiveEntity::new(id.clone(), data, self))),
350            Err(_) => Ok(None),
351        }
352    }
353
354    /// Retrieve all entities wrapped for change tracking.
355    ///
356    /// Each returned `entity` operates independently - modifications to one
357    /// entity don't affect others, and each must be saved separately.
358    ///
359    /// # Performance Note
360    ///
361    /// This loads and deserializes all entities into memory. Consider pagination
362    /// or streaming approaches for large datasets.
363    async fn list_entities(&self) -> Result<Vec<ActiveEntity<'_, Self, E>>> {
364        let items = self.list().await?;
365
366        Ok(items
367            .into_iter()
368            .map(|(id, data)| ActiveEntity::new(id, data, self))
369            .collect::<Vec<_>>())
370    }
371
372    /// Retrieve some entity wrapped for change tracking and deferred persistence.
373    ///
374    /// This is equivalent to get_some() but returns an ActiveEntity wrapper.
375    ///
376    /// # Returns
377    ///
378    /// - `Ok(Some(entity))`: Entity wrapper with change tracking
379    /// - `Ok(None)`: no entities exist in the dataset
380    /// - `Err`: Storage or deserialization error
381    async fn get_some_entity(&self) -> Result<Option<ActiveEntity<'_, Self, E>>> {
382        match self.get_some().await? {
383            Some((id, data)) => Ok(Some(ActiveEntity::new(id, data, self))),
384            None => Ok(None),
385        }
386    }
387
388    /// Create a new entity with the provided data.
389    ///
390    /// This method creates a new entity and returns it wrapped as an ActiveEntity.
391    /// The entity is not automatically saved - call `.save()` to persist it.
392    ///
393    /// # Parameters
394    ///
395    /// - `id`: The ID for the new entity
396    /// - `entity`: The entity data
397    ///
398    /// # Returns
399    ///
400    /// - `ActiveEntity`: New entity wrapper ready for modification and saving
401    ///
402    /// # Example
403    ///
404    /// ```rust,ignore
405    /// let mut user = table.get_entity(&"user123".to_string()).await?
406    ///     .unwrap_or_else(|| table.new_entity("user123".to_string(), User {
407    ///         id: Some("user123".to_string()),
408    ///         name: "Default User".to_string(),
409    ///         active: false,
410    ///     }));
411    ///
412    /// user.active = true;
413    /// user.save().await?;
414    /// ```
415    ///
416    /// # Note
417    ///
418    /// This method does not check if an entity with the given ID already exists.
419    /// Use in combination with `get_entity()` for get-or-create patterns.
420    fn new_entity(&self, id: Self::Id, entity: E) -> ActiveEntity<'_, Self, E> {
421        ActiveEntity::new(id, entity, self)
422    }
423}
424// Auto-implement for any type that has both readable and writable traits
425impl<T, E> ActiveEntitySet<E> for T
426where
427    T: ReadableDataSet<E> + WritableDataSet<E>,
428    E: Entity<T::Value>
429        + vantage_types::IntoRecord<T::Value>
430        + vantage_types::TryFromRecord<T::Value>
431        + Send
432        + Sync
433        + Clone,
434{
435}
436
437// // Auto-implement for any type that has both readable and writable traits
438// #[async_trait]
439// impl<T> entityValueSet for T
440// where
441//     T: ReadableValueSet + WritableValueSet,
442//     Self::Value: Send + Sync + Clone,
443// {
444//     async fn get_value_entity(&self, id: &Self::Id) -> Result<entityValue<'_, Self>> {
445//         let value = self.get_value(id).await?;
446//         Ok(entityValue::new(id, value, self))
447//     }
448
449//     async fn list_value_entities(&self) -> Result<Vec<entityValue<'_, Self>>> {
450//         let items = self.list_values().await?;
451
452//         Ok(items
453//             .into_iter()
454//             .map(|(id, value)| entityValue::new(id, value, self))
455//             .collect::<Vec<_>>())
456//     }
457// }