Skip to main content

usenet_dl/db/
rss.rs

1//! RSS feed CRUD operations.
2
3use crate::error::DatabaseError;
4use crate::{Error, Result};
5
6use super::{
7    Database, InsertRssFeedParams, InsertRssFilterParams, RssFeed, RssFilterRow,
8    UpdateRssFeedParams,
9};
10
11impl Database {
12    /// Get all RSS feeds
13    pub async fn get_all_rss_feeds(&self) -> Result<Vec<RssFeed>> {
14        let feeds = sqlx::query_as::<_, RssFeed>(
15            r#"
16            SELECT id, name, url, check_interval_secs, category, auto_download,
17                   priority, enabled, last_check, last_error, created_at
18            FROM rss_feeds
19            ORDER BY id ASC
20            "#,
21        )
22        .fetch_all(&self.pool)
23        .await
24        .map_err(|e| {
25            Error::Database(DatabaseError::QueryFailed(format!(
26                "Failed to get RSS feeds: {}",
27                e
28            )))
29        })?;
30
31        Ok(feeds)
32    }
33
34    /// Get RSS feed by ID
35    pub async fn get_rss_feed(&self, id: i64) -> Result<Option<RssFeed>> {
36        let feed = sqlx::query_as::<_, RssFeed>(
37            r#"
38            SELECT id, name, url, check_interval_secs, category, auto_download,
39                   priority, enabled, last_check, last_error, created_at
40            FROM rss_feeds
41            WHERE id = ?
42            "#,
43        )
44        .bind(id)
45        .fetch_optional(&self.pool)
46        .await
47        .map_err(|e| {
48            Error::Database(DatabaseError::QueryFailed(format!(
49                "Failed to get RSS feed: {}",
50                e
51            )))
52        })?;
53
54        Ok(feed)
55    }
56
57    /// Insert a new RSS feed
58    pub async fn insert_rss_feed(&self, params: InsertRssFeedParams<'_>) -> Result<i64> {
59        let InsertRssFeedParams {
60            name,
61            url,
62            check_interval_secs,
63            category,
64            auto_download,
65            priority,
66            enabled,
67        } = params;
68        let now = chrono::Utc::now().timestamp();
69
70        let result = sqlx::query(
71            r#"
72            INSERT INTO rss_feeds (name, url, check_interval_secs, category, auto_download,
73                                  priority, enabled, created_at)
74            VALUES (?, ?, ?, ?, ?, ?, ?, ?)
75            "#,
76        )
77        .bind(name)
78        .bind(url)
79        .bind(check_interval_secs)
80        .bind(category)
81        .bind(auto_download as i32)
82        .bind(priority)
83        .bind(enabled as i32)
84        .bind(now)
85        .execute(&self.pool)
86        .await
87        .map_err(|e| {
88            Error::Database(DatabaseError::QueryFailed(format!(
89                "Failed to insert RSS feed: {}",
90                e
91            )))
92        })?;
93
94        Ok(result.last_insert_rowid())
95    }
96
97    /// Update an existing RSS feed
98    pub async fn update_rss_feed(&self, params: UpdateRssFeedParams<'_>) -> Result<bool> {
99        let UpdateRssFeedParams {
100            id,
101            name,
102            url,
103            check_interval_secs,
104            category,
105            auto_download,
106            priority,
107            enabled,
108        } = params;
109        let result = sqlx::query(
110            r#"
111            UPDATE rss_feeds
112            SET name = ?, url = ?, check_interval_secs = ?, category = ?,
113                auto_download = ?, priority = ?, enabled = ?
114            WHERE id = ?
115            "#,
116        )
117        .bind(name)
118        .bind(url)
119        .bind(check_interval_secs)
120        .bind(category)
121        .bind(auto_download as i32)
122        .bind(priority)
123        .bind(enabled as i32)
124        .bind(id)
125        .execute(&self.pool)
126        .await
127        .map_err(|e| {
128            Error::Database(DatabaseError::QueryFailed(format!(
129                "Failed to update RSS feed: {}",
130                e
131            )))
132        })?;
133
134        Ok(result.rows_affected() > 0)
135    }
136
137    /// Delete an RSS feed (cascades to filters and seen items)
138    pub async fn delete_rss_feed(&self, id: i64) -> Result<bool> {
139        let result = sqlx::query("DELETE FROM rss_feeds WHERE id = ?")
140            .bind(id)
141            .execute(&self.pool)
142            .await
143            .map_err(|e| {
144                Error::Database(DatabaseError::QueryFailed(format!(
145                    "Failed to delete RSS feed: {}",
146                    e
147                )))
148            })?;
149
150        Ok(result.rows_affected() > 0)
151    }
152
153    /// Get all filters for a specific RSS feed
154    pub async fn get_rss_filters(&self, feed_id: i64) -> Result<Vec<RssFilterRow>> {
155        let filters = sqlx::query_as::<_, RssFilterRow>(
156            r#"
157            SELECT id, feed_id, name, include_patterns, exclude_patterns,
158                   min_size, max_size, max_age_secs
159            FROM rss_filters
160            WHERE feed_id = ?
161            ORDER BY id ASC
162            "#,
163        )
164        .bind(feed_id)
165        .fetch_all(&self.pool)
166        .await
167        .map_err(|e| {
168            Error::Database(DatabaseError::QueryFailed(format!(
169                "Failed to get RSS filters: {}",
170                e
171            )))
172        })?;
173
174        Ok(filters)
175    }
176
177    /// Insert a new RSS filter
178    pub async fn insert_rss_filter(&self, params: InsertRssFilterParams<'_>) -> Result<i64> {
179        let InsertRssFilterParams {
180            feed_id,
181            name,
182            include_patterns,
183            exclude_patterns,
184            min_size,
185            max_size,
186            max_age_secs,
187        } = params;
188        let result = sqlx::query(
189            r#"
190            INSERT INTO rss_filters (feed_id, name, include_patterns, exclude_patterns,
191                                    min_size, max_size, max_age_secs)
192            VALUES (?, ?, ?, ?, ?, ?, ?)
193            "#,
194        )
195        .bind(feed_id)
196        .bind(name)
197        .bind(include_patterns)
198        .bind(exclude_patterns)
199        .bind(min_size)
200        .bind(max_size)
201        .bind(max_age_secs)
202        .execute(&self.pool)
203        .await
204        .map_err(|e| {
205            Error::Database(DatabaseError::QueryFailed(format!(
206                "Failed to insert RSS filter: {}",
207                e
208            )))
209        })?;
210
211        Ok(result.last_insert_rowid())
212    }
213
214    /// Delete all filters for a feed (used during update)
215    pub async fn delete_rss_filters(&self, feed_id: i64) -> Result<()> {
216        sqlx::query("DELETE FROM rss_filters WHERE feed_id = ?")
217            .bind(feed_id)
218            .execute(&self.pool)
219            .await
220            .map_err(|e| {
221                Error::Database(DatabaseError::QueryFailed(format!(
222                    "Failed to delete RSS filters: {}",
223                    e
224                )))
225            })?;
226
227        Ok(())
228    }
229
230    /// Update last check time and error for an RSS feed
231    pub async fn update_rss_feed_check_status(
232        &self,
233        id: i64,
234        last_error: Option<&str>,
235    ) -> Result<()> {
236        let now = chrono::Utc::now().timestamp();
237
238        sqlx::query(
239            r#"
240            UPDATE rss_feeds
241            SET last_check = ?, last_error = ?
242            WHERE id = ?
243            "#,
244        )
245        .bind(now)
246        .bind(last_error)
247        .bind(id)
248        .execute(&self.pool)
249        .await
250        .map_err(|e| {
251            Error::Database(DatabaseError::QueryFailed(format!(
252                "Failed to update RSS feed status: {}",
253                e
254            )))
255        })?;
256
257        Ok(())
258    }
259}