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// }