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