aboutsummaryrefslogtreecommitdiff
path: root/src/infrastructure/persistence/calendar_repository.rs
blob: d0b758ddf7678d492e6b2612b20c65d1442a86ca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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(())
    }
}