Skip to main content

vantage_dataset/im/
valueset_writable.rs

1use async_trait::async_trait;
2use vantage_types::{Entity, Record};
3
4use crate::{im::ImTable, traits::WritableValueSet};
5
6#[async_trait]
7impl<E> WritableValueSet for ImTable<E>
8where
9    E: Entity,
10{
11    async fn insert_value(
12        &self,
13        id: &Self::Id,
14        record: &Record<Self::Value>,
15    ) -> crate::traits::Result<Record<Self::Value>> {
16        let mut table = self.data_source.get_or_create_table(&self.table_name);
17
18        // Check if record already exists (idempotent behavior)
19        if let Some(existing_record) = table.get(id) {
20            return Ok(existing_record.clone());
21        }
22
23        table.insert(id.clone(), record.clone());
24        self.data_source.update_table(&self.table_name, table);
25
26        Ok(record.clone())
27    }
28
29    async fn replace_value(
30        &self,
31        id: &Self::Id,
32        record: &Record<Self::Value>,
33    ) -> crate::traits::Result<Record<Self::Value>> {
34        let mut table = self.data_source.get_or_create_table(&self.table_name);
35
36        table.insert(id.clone(), record.clone());
37        self.data_source.update_table(&self.table_name, table);
38
39        Ok(record.clone())
40    }
41
42    async fn patch_value(
43        &self,
44        id: &Self::Id,
45        partial: &Record<Self::Value>,
46    ) -> crate::traits::Result<Record<Self::Value>> {
47        let mut table = self.data_source.get_or_create_table(&self.table_name);
48
49        // Check if record exists
50        let mut existing_record = table
51            .get(id)
52            .ok_or_else(|| {
53                vantage_core::util::error::vantage_error!("Record with id '{}' not found", id)
54            })?
55            .clone();
56
57        // Merge the partial fields into the existing record
58        for (key, value) in partial.iter() {
59            existing_record.insert(key.clone(), value.clone());
60        }
61
62        table.insert(id.clone(), existing_record.clone());
63        self.data_source.update_table(&self.table_name, table);
64
65        Ok(existing_record)
66    }
67
68    async fn delete(&self, id: &Self::Id) -> crate::traits::Result<()> {
69        let mut table = self.data_source.get_or_create_table(&self.table_name);
70
71        // Delete is idempotent - success even if record doesn't exist
72        table.shift_remove(id);
73
74        self.data_source.update_table(&self.table_name, table);
75        Ok(())
76    }
77
78    async fn delete_all(&self) -> crate::traits::Result<()> {
79        let mut table = self.data_source.get_or_create_table(&self.table_name);
80        table.clear();
81        self.data_source.update_table(&self.table_name, table);
82        Ok(())
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::im::ImDataSource;
90    use serde::{Deserialize, Serialize};
91    #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
92    struct User {
93        id: Option<String>,
94        name: String,
95    }
96
97    #[tokio::test]
98    async fn test_replace_value() {
99        let ds = ImDataSource::new();
100        let table = ImTable::<User>::new(&ds, "users");
101
102        let mut record = Record::new();
103        record.insert(
104            "name".to_string(),
105            serde_json::Value::String("Alice".to_string()),
106        );
107        table
108            .replace_value(&"user1".to_string(), &record)
109            .await
110            .unwrap();
111    }
112
113    #[tokio::test]
114    async fn test_patch_value() {
115        let ds = ImDataSource::new();
116        let table = ImTable::<User>::new(&ds, "users");
117
118        // Patch non-existent record should fail
119        let mut patch = Record::new();
120        patch.insert(
121            "name".to_string(),
122            serde_json::Value::String("Updated".to_string()),
123        );
124        let result = table.patch_value(&"nonexistent".to_string(), &patch).await;
125        assert!(result.is_err());
126    }
127
128    #[tokio::test]
129    async fn test_delete() {
130        let ds = ImDataSource::new();
131        let table = ImTable::<User>::new(&ds, "users");
132
133        // Delete non-existent record should succeed (idempotent)
134        table.delete(&"nonexistent".to_string()).await.unwrap();
135    }
136
137    #[tokio::test]
138    async fn test_delete_all() {
139        let ds = ImDataSource::new();
140        let table = ImTable::<User>::new(&ds, "users");
141
142        table.delete_all().await.unwrap();
143    }
144}