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: Option<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    /// Returns `Ok(None)` when no entity exists with the given ID. The storage
119    /// value is automatically deserialized to the entity type.
120    ///
121    /// # Errors
122    ///
123    /// Returns an error only if the lookup itself fails (connection errors,
124    /// deserialization failures). A missing row is not an error.
125    async fn get(&self, id: impl Into<Self::Id> + Send) -> Result<Option<E>>;
126
127    /// Retrieve one single entity from the set. If entities are ordered - return first entity.
128    ///
129    /// Useful for sampling data or checking if the dataset contains any entities.
130    /// Returns `None` if the dataset is empty.
131    async fn get_some(&self) -> Result<Option<(Self::Id, E)>>;
132}
133
134/// Write operations on typed entities with automatic serialization.
135///
136/// This trait provides convenient write operations that automatically handle
137/// entity serialization to the storage format. All operations follow idempotent
138/// patterns safe for retry in distributed systems.
139///
140/// # Serialization Behavior
141///
142/// Entities are automatically serialized to the storage's `Value` type before
143/// persistence. The serialization format depends on your storage backend:
144/// - JSON databases use `serde_json` serialization
145/// - Binary stores may use CBOR or custom formats
146/// - Document databases preserve nested structure
147///
148/// # Idempotency Guarantees
149///
150/// All write operations are designed to be safely retryable:
151/// - `insert`: No-op if ID already exists
152/// - `replace`: Always succeeds, overwrites existing data
153/// - `patch`: Atomic update, fails if entity doesn't exist
154///
155/// # Example
156///
157/// ```rust,ignore
158/// use vantage_dataset::dataset::WritableDataSet;
159///
160/// let user = User {
161///     name: "Alice".to_string(),
162///     email: "alice@example.com".to_string(),
163///     age: 30,
164/// };
165///
166/// // Idempotent insert
167/// users.insert(&"user-123".to_string(), user.clone()).await?;
168///
169/// // Update specific fields
170/// let mut updated_user = user;
171/// updated_user.age = 31;
172/// users.replace(&"user-123".to_string(), updated_user).await?;
173/// ```
174#[async_trait]
175pub trait WritableDataSet<E>: DataSet<E>
176where
177    E: Entity<Self::Value>,
178{
179    /// Insert entity with a specific ID (often generated) (HTTP POST with ID)
180    ///
181    /// **Idempotent**: Succeeds if no entity exists with the given ID. If
182    /// entity already exists, must return success without overwriting
183    /// data, returning original data.
184    ///
185    /// **Returns**: Entity as it was stored.
186    ///
187    /// # Use Case
188    /// Generate unique ID and store centity while avoiding duplicates.
189    async fn insert(&self, id: &Self::Id, entity: &E) -> Result<E>;
190
191    /// Replace the entire entity at the specified ID (HTTP PUT)
192    ///
193    /// **Idempotent**: Always succeeds, completely overwrites existing data
194    /// if present. If possible, will remove/recreate entity; therefore if
195    /// `entity` doesn't contain certain attributes which were present in the
196    /// database, those will be removed. If entity does not exist, will
197    /// create it.
198    ///
199    /// **Returns**: entity as it was stored.
200    ///
201    /// # Use Case
202    /// Replace with a new version of a entity.
203    async fn replace(&self, id: &Self::Id, entity: &E) -> Result<E>;
204
205    /// Partially update an entity by merging with the provided data (HTTP PATCH)
206    ///
207    /// **Fails if entity doesn't exist**. The exact merge behavior depends on
208    /// the storage implementation - typically merges object fields for JSON-like values.
209    ///
210    /// **Returns**: entity as it was stored (not only the partial change).
211    ///
212    /// # Use Case
213    /// Update only the modified fields of a entity.
214    async fn patch(&self, id: &Self::Id, partial: &E) -> Result<E>;
215}
216
217/// Append-only operations with automatic ID generation.
218///
219/// This trait is designed for storage backends that naturally generate unique IDs
220/// for new entities, such as message queues, event streams, or auto-incrementing
221/// database tables.
222///
223/// # Idempotency Considerations
224///
225/// Unlike other dataset operations, `insert_return_id` is **not idempotent** because
226/// each call generates a new ID. Use this pattern only when:
227/// - Your system can handle duplicate entities (event sourcing)
228/// - You have application-level deduplication
229/// - The storage naturally handles uniqueness (like message queues)
230///
231/// For idempotent operations, prefer [`WritableDataSet::insert`] with predetermined IDs.
232///
233/// # Example
234///
235/// ```rust,ignore
236/// use vantage_dataset::dataset::InsertableDataSet;
237///
238/// // Message queue scenario - each event gets unique ID
239/// let event = UserLoginEvent {
240///     user_id: "user-123".to_string(),
241///     timestamp: Utc::now(),
242///     ip_address: "192.168.1.1".to_string(),
243/// };
244///
245/// let event_id = events.insert_return_id(event).await?;
246/// println!("Generated event ID: {}", event_id);
247/// ```
248#[async_trait]
249pub trait InsertableDataSet<E>: DataSet<E>
250where
251    E: Entity<Self::Value>,
252{
253    /// Insert an entity and return the generated ID.
254    ///
255    /// The storage backend generates a unique identifier for the new entity.
256    /// The entity is automatically serialized to the storage format.
257    ///
258    /// # Warning
259    ///
260    /// This method is **not idempotent** - each call creates a new entity with
261    /// a new ID, even if the entity data is identical.
262    async fn insert_return_id(&self, entity: &E) -> Result<Self::Id>;
263}
264
265/// Change tracking for typed entities with automatic persistence.
266///
267/// This trait extends readable and writable datasets with a "entity" pattern that
268/// tracks entity modifications and enables deferred persistence. entities act as
269/// smart wrappers around entities that know how to save themselves back to storage.
270///
271/// # entity Pattern Benefits
272///
273/// - **Change tracking**: Only modified fields are serialized and persisted
274/// - **Type safety**: Work with native Rust entities, not raw values
275/// - **Optimistic locking**: Conflict detection in concurrent scenarios
276/// - **Deferred persistence**: Batch multiple changes before saving
277/// - **Interactive editing**: Perfect for UI scenarios with undo/redo
278///
279/// # Example
280///
281/// ```rust,ignore
282/// use vantage_dataset::dataset::entityDataSet;
283///
284/// // Get entity for interactive editing
285/// let mut user_entity = users.get_entity(&user_id).await?.unwrap();
286///
287/// // Modify through standard field access
288/// user_entity.name = "Alice Smith".to_string();
289/// user_entity.age = 31;
290///
291/// // Changes are automatically tracked and persisted
292/// user_entity.save().await?;
293///
294/// // Or work with multiple entities
295/// let mut entities = users.list_entities().await?;
296/// for mut entity in entities {
297///     entity.status = Status::Processed;
298///     entity.save().await?; // Each saves independently
299/// }
300/// ```
301#[async_trait]
302pub trait ActiveEntitySet<E>: ReadableDataSet<E> + WritableDataSet<E>
303where
304    E: Entity<Self::Value>
305        + vantage_types::IntoRecord<Self::Value>
306        + vantage_types::TryFromRecord<Self::Value>
307        + Send
308        + Sync
309        + Clone,
310{
311    /// Retrieve an entity wrapped for change tracking and deferred persistence.
312    ///
313    /// The returned `entity` can be modified in-place and will track all
314    /// changes for efficient persistence when `save()` is called.
315    ///
316    /// # Example
317    ///
318    /// ```rust,ignore
319    /// let mut user = table.get_entity(&"user123".to_string()).await?
320    ///     .unwrap_or_else(|| table.new_entity("user123".to_string(), User {
321    ///         id: Some("user123".to_string()),
322    ///         name: "Default User".to_string(),
323    ///         active: false,
324    ///     }));
325    ///
326    /// user.active = true;
327    /// user.save().await?;
328    /// ```
329    ///
330    /// # Returns
331    ///
332    /// - `Ok(Some(entity))`: Entity wrapper with change tracking
333    /// - `Ok(None)`: entity doesn't exist
334    /// - `Err`: Storage or deserialization error
335    async fn get_entity(&self, id: &Self::Id) -> Result<Option<ActiveEntity<'_, Self, E>>> {
336        Ok(self
337            .get(id.clone())
338            .await?
339            .map(|data| ActiveEntity::new(id.clone(), data, self)))
340    }
341
342    /// Retrieve all entities wrapped for change tracking.
343    ///
344    /// Each returned `entity` operates independently - modifications to one
345    /// entity don't affect others, and each must be saved separately.
346    ///
347    /// # Performance Note
348    ///
349    /// This loads and deserializes all entities into memory. Consider pagination
350    /// or streaming approaches for large datasets.
351    async fn list_entities(&self) -> Result<Vec<ActiveEntity<'_, Self, E>>> {
352        let items = self.list().await?;
353
354        Ok(items
355            .into_iter()
356            .map(|(id, data)| ActiveEntity::new(id, data, self))
357            .collect::<Vec<_>>())
358    }
359
360    /// Retrieve some entity wrapped for change tracking and deferred persistence.
361    ///
362    /// This is equivalent to get_some() but returns an ActiveEntity wrapper.
363    ///
364    /// # Returns
365    ///
366    /// - `Ok(Some(entity))`: Entity wrapper with change tracking
367    /// - `Ok(None)`: no entities exist in the dataset
368    /// - `Err`: Storage or deserialization error
369    async fn get_some_entity(&self) -> Result<Option<ActiveEntity<'_, Self, E>>> {
370        match self.get_some().await? {
371            Some((id, data)) => Ok(Some(ActiveEntity::new(id, data, self))),
372            None => Ok(None),
373        }
374    }
375
376    /// Create a new entity with the provided data.
377    ///
378    /// This method creates a new entity and returns it wrapped as an ActiveEntity.
379    /// The entity is not automatically saved - call `.save()` to persist it.
380    ///
381    /// # Parameters
382    ///
383    /// - `id`: The ID for the new entity
384    /// - `entity`: The entity data
385    ///
386    /// # Returns
387    ///
388    /// - `ActiveEntity`: New entity wrapper ready for modification and saving
389    ///
390    /// # Example
391    ///
392    /// ```rust,ignore
393    /// let mut user = table.get_entity(&"user123".to_string()).await?
394    ///     .unwrap_or_else(|| table.new_entity("user123".to_string(), User {
395    ///         id: Some("user123".to_string()),
396    ///         name: "Default User".to_string(),
397    ///         active: false,
398    ///     }));
399    ///
400    /// user.active = true;
401    /// user.save().await?;
402    /// ```
403    ///
404    /// # Note
405    ///
406    /// This method does not check if an entity with the given ID already exists.
407    /// Use in combination with `get_entity()` for get-or-create patterns.
408    fn new_entity(&self, id: Self::Id, entity: E) -> ActiveEntity<'_, Self, E> {
409        ActiveEntity::new(id, entity, self)
410    }
411}
412// Auto-implement for any type that has both readable and writable traits
413impl<T, E> ActiveEntitySet<E> for T
414where
415    T: ReadableDataSet<E> + WritableDataSet<E>,
416    E: Entity<T::Value>
417        + vantage_types::IntoRecord<T::Value>
418        + vantage_types::TryFromRecord<T::Value>
419        + Send
420        + Sync
421        + Clone,
422{
423}
424
425// // Auto-implement for any type that has both readable and writable traits
426// #[async_trait]
427// impl<T> entityValueSet for T
428// where
429//     T: ReadableValueSet + WritableValueSet,
430//     Self::Value: Send + Sync + Clone,
431// {
432//     async fn get_value_entity(&self, id: &Self::Id) -> Result<entityValue<'_, Self>> {
433//         let value = self.get_value(id).await?;
434//         Ok(entityValue::new(id, value, self))
435//     }
436
437//     async fn list_value_entities(&self) -> Result<Vec<entityValue<'_, Self>>> {
438//         let items = self.list_values().await?;
439
440//         Ok(items
441//             .into_iter()
442//             .map(|(id, value)| entityValue::new(id, value, self))
443//             .collect::<Vec<_>>())
444//     }
445// }