diff options
| author | Mikkel Thestrup <mikkel@mithe.dk> | 2026-01-27 17:07:13 +0100 |
|---|---|---|
| committer | Mikkel Thestrup <mikkel@mithe.dk> | 2026-01-27 17:08:24 +0100 |
| commit | b35c8cca57811050536a4fa6c1cb5675453ad463 (patch) | |
| tree | 622bce9ef4021701ab845fd88ff0fc86b47905aa /src/infrastructure/persistence/calendar_repository.rs | |
| parent | 4e78fd83349c95711cdee5acc56f248f81ebd25c (diff) | |
| download | kal-b35c8cca57811050536a4fa6c1cb5675453ad463.tar.gz kal-b35c8cca57811050536a4fa6c1cb5675453ad463.zip | |
feat(infrastructure): implement SQLite persistence layer for calendar domain
Add infrastructure layer with SQLite repositories for
calendars, events, and recurring events. Implements the repository
pattern with proper domain/infrastructure separation.
- Add CalendarModel, EventModel, RecurrenceModel, and RecurrenceExceptionModel
for database persistence
- Implement SqliteCalendarRepository with CRUD operations
- Implement SqliteEventRepository with calendar filtering and time range queries
- Implement SqliteRecurringEventRepository with exception handling and transactions
- Add bidirectional mappers between domain entities and persistence models
- Use sqlx query_as for type-safe database queries with FromRow derivation
- Support upsert operations for all entities using ON CONFLICT clauses
Diffstat (limited to '')
| -rw-r--r-- | src/infrastructure/persistence/calendar_repository.rs | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/src/infrastructure/persistence/calendar_repository.rs b/src/infrastructure/persistence/calendar_repository.rs new file mode 100644 index 0000000..d0b758d --- /dev/null +++ b/src/infrastructure/persistence/calendar_repository.rs @@ -0,0 +1,119 @@ +use async_trait::async_trait; +use sqlx::SqlitePool; +use crate::{ + domain::{ + calendar::Calendar, + repository::{CalendarRepository, RepositoryError, Result}, + value_objects::CalendarId, + }, + infrastructure::persistence::models::CalendarModel +}; +use super::mappers::CalendarMapper; + +pub struct SqliteCalendarRepository { + pool: SqlitePool, +} + +impl SqliteCalendarRepository { + pub fn new(pool: SqlitePool) -> Self { + Self { pool } + } +} + +#[async_trait] +impl CalendarRepository for SqliteCalendarRepository { + async fn save(&self, calendar: &Calendar) -> Result<()> { + let model = CalendarMapper::to_model(calendar); + + sqlx::query!( + r#" + INSERT INTO calendars ( + id, name, description, is_archived, + created_at, updated_at + ) + VALUES (?1, ?2, ?3, ?4, ?5, ?6) + ON CONFLICT(id) DO UPDATE SET + name = excluded.name, + description = excluded.description, + is_archived = excluded.is_archived, + updated_at = excluded.updated_at + "#, + model.id, + model.name, + model.description, + model.is_archived, + model.created_at, + model.updated_at, + ) + .execute(&self.pool) + .await + .map_err(|e| RepositoryError::DatabaseError(e.to_string()))?; + + Ok(()) + } + + async fn find_by_id(&self, id: &CalendarId) -> Result<Option<Calendar>> { + let id_str = id.to_string(); + + let model = sqlx::query_as::<_, CalendarModel>( + r#" + SELECT id, name, description, is_archived, created_at, updated_at + FROM calendars + WHERE id = ?1 + "# + ) + .bind(&id_str) + .fetch_optional(&self.pool) + .await + .map_err(|e| RepositoryError::DatabaseError(e.to_string()))?; + + match model { + Some(m) => { + let calendar = CalendarMapper::to_domain(m) + .map_err(|e| RepositoryError::DatabaseError(e))?; + Ok(Some(calendar)) + } + None => Ok(None), + } + } + + async fn find_all_active(&self) -> Result<Vec<Calendar>> { + let models = sqlx::query_as::<_, CalendarModel>( + r#" + SELECT id, name, description, is_archived, created_at, updated_at + FROM calendars + WHERE is_archived = 0 + ORDER BY name + "# + ) + .fetch_all(&self.pool) + .await + .map_err(|e| RepositoryError::DatabaseError(e.to_string()))?; + + models + .into_iter() + .map(|m| CalendarMapper::to_domain(m) + .map_err(|e| RepositoryError::DatabaseError(e))) + .collect() + } + + async fn delete(&self, id: &CalendarId) -> Result<()> { + let id_str = id.to_string(); + + let result = sqlx::query!( + r#" + DELETE FROM calendars WHERE id = ?1 + "#, + id_str, + ) + .execute(&self.pool) + .await + .map_err(|e| RepositoryError::DatabaseError(e.to_string()))?; + + if result.rows_affected() == 0 { + return Err(RepositoryError::NotFound); + } + + Ok(()) + } +} |