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", user.clone()).await?;
168///
169/// // Update specific fields
170/// let mut updated_user = user;
171/// updated_user.age = 31;
172/// users.replace("user-123", 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: impl Into<Self::Id> + Send, 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: impl Into<Self::Id> + Send, 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: impl Into<Self::Id> + Send, 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::TryIntoRecord<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(
336        &self,
337        id: impl Into<Self::Id> + Send,
338    ) -> Result<Option<ActiveEntity<'_, Self, E>>> {
339        let id = id.into();
340        Ok(self
341            .get(id.clone())
342            .await?
343            .map(|data| ActiveEntity::new(id, data, self)))
344    }
345
346    /// Retrieve all entities wrapped for change tracking.
347    ///
348    /// Each returned `entity` operates independently - modifications to one
349    /// entity don't affect others, and each must be saved separately.
350    ///
351    /// # Performance Note
352    ///
353    /// This loads and deserializes all entities into memory. Consider pagination
354    /// or streaming approaches for large datasets.
355    async fn list_entities(&self) -> Result<Vec<ActiveEntity<'_, Self, E>>> {
356        let items = self.list().await?;
357
358        Ok(items
359            .into_iter()
360            .map(|(id, data)| ActiveEntity::new(id, data, self))
361            .collect::<Vec<_>>())
362    }
363
364    /// Retrieve some entity wrapped for change tracking and deferred persistence.
365    ///
366    /// This is equivalent to get_some() but returns an ActiveEntity wrapper.
367    ///
368    /// # Returns
369    ///
370    /// - `Ok(Some(entity))`: Entity wrapper with change tracking
371    /// - `Ok(None)`: no entities exist in the dataset
372    /// - `Err`: Storage or deserialization error
373    async fn get_some_entity(&self) -> Result<Option<ActiveEntity<'_, Self, E>>> {
374        match self.get_some().await? {
375            Some((id, data)) => Ok(Some(ActiveEntity::new(id, data, self))),
376            None => Ok(None),
377        }
378    }
379
380    /// Create a new entity with the provided data.
381    ///
382    /// This method creates a new entity and returns it wrapped as an ActiveEntity.
383    /// The entity is not automatically saved - call `.save()` to persist it.
384    ///
385    /// # Parameters
386    ///
387    /// - `id`: The ID for the new entity
388    /// - `entity`: The entity data
389    ///
390    /// # Returns
391    ///
392    /// - `ActiveEntity`: New entity wrapper ready for modification and saving
393    ///
394    /// # Example
395    ///
396    /// ```rust,ignore
397    /// let mut user = table.get_entity(&"user123".to_string()).await?
398    ///     .unwrap_or_else(|| table.new_entity("user123".to_string(), User {
399    ///         id: Some("user123".to_string()),
400    ///         name: "Default User".to_string(),
401    ///         active: false,
402    ///     }));
403    ///
404    /// user.active = true;
405    /// user.save().await?;
406    /// ```
407    ///
408    /// # Note
409    ///
410    /// This method does not check if an entity with the given ID already exists.
411    /// Use in combination with `get_entity()` for get-or-create patterns.
412    fn new_entity(&self, id: Self::Id, entity: E) -> ActiveEntity<'_, Self, E> {
413        ActiveEntity::new(id, entity, self)
414    }
415}
416// Auto-implement for any type that has both readable and writable traits
417impl<T, E> ActiveEntitySet<E> for T
418where
419    T: ReadableDataSet<E> + WritableDataSet<E>,
420    E: Entity<T::Value>
421        + vantage_types::TryIntoRecord<T::Value>
422        + vantage_types::TryFromRecord<T::Value>
423        + Send
424        + Sync
425        + Clone,
426{
427}
428
429// // Auto-implement for any type that has both readable and writable traits
430// #[async_trait]
431// impl<T> entityValueSet for T
432// where
433//     T: ReadableValueSet + WritableValueSet,
434//     Self::Value: Send + Sync + Clone,
435// {
436//     async fn get_value_entity(&self, id: &Self::Id) -> Result<entityValue<'_, Self>> {
437//         let value = self.get_value(id).await?;
438//         Ok(entityValue::new(id, value, self))
439//     }
440
441//     async fn list_value_entities(&self) -> Result<Vec<entityValue<'_, Self>>> {
442//         let items = self.list_values().await?;
443
444//         Ok(items
445//             .into_iter()
446//             .map(|(id, value)| entityValue::new(id, value, self))
447//             .collect::<Vec<_>>())
448//     }
449// }