From 50ce8cb96b2b218751c2fc2a6b19372f51846acc Mon Sep 17 00:00:00 2001 From: Dawid Rycerz Date: Mon, 14 Jul 2025 19:34:59 +0300 Subject: feat: rewrite in rust --- src/weather_thresholds.rs | 165 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 src/weather_thresholds.rs (limited to 'src/weather_thresholds.rs') diff --git a/src/weather_thresholds.rs b/src/weather_thresholds.rs new file mode 100644 index 0000000..bfb9cdf --- /dev/null +++ b/src/weather_thresholds.rs @@ -0,0 +1,165 @@ +use serde::{Deserialize, Serialize}; +use sqlx::FromRow; + +#[derive(Debug, Serialize, Deserialize, FromRow, Clone, PartialEq)] +pub struct WeatherThreshold { + pub id: i64, + pub user_id: i64, + pub condition_type: String, + pub threshold_value: f64, + pub operator: String, + pub enabled: bool, + pub description: Option, +} + +pub struct WeatherThresholdRepository<'a> { + pub db: &'a sqlx::SqlitePool, +} + +impl<'a> WeatherThresholdRepository<'a> { + pub async fn list_thresholds(&self, user_id: i64) -> Result, sqlx::Error> { + sqlx::query_as::<_, WeatherThreshold>( + "SELECT id, user_id, condition_type, threshold_value, operator, enabled, description FROM weather_thresholds WHERE user_id = ?" + ) + .bind(user_id) + .fetch_all(self.db) + .await + } + + pub async fn get_threshold(&self, id: i64, user_id: i64) -> Result, sqlx::Error> { + sqlx::query_as::<_, WeatherThreshold>( + "SELECT id, user_id, condition_type, threshold_value, operator, enabled, description FROM weather_thresholds WHERE id = ? AND user_id = ?" + ) + .bind(id) + .bind(user_id) + .fetch_optional(self.db) + .await + } + + pub async fn create_threshold(&self, user_id: i64, condition_type: String, threshold_value: f64, operator: String, enabled: bool, description: Option) -> Result { + sqlx::query_as::<_, WeatherThreshold>( + "INSERT INTO weather_thresholds (user_id, condition_type, threshold_value, operator, enabled, description) VALUES (?, ?, ?, ?, ?, ?) RETURNING id, user_id, condition_type, threshold_value, operator, enabled, description" + ) + .bind(user_id) + .bind(condition_type) + .bind(threshold_value) + .bind(operator) + .bind(enabled) + .bind(description) + .fetch_one(self.db) + .await + } + + pub async fn update_threshold(&self, id: i64, user_id: i64, condition_type: String, threshold_value: f64, operator: String, enabled: bool, description: Option) -> Result { + sqlx::query_as::<_, WeatherThreshold>( + "UPDATE weather_thresholds SET condition_type = ?, threshold_value = ?, operator = ?, enabled = ?, description = ? WHERE id = ? AND user_id = ? RETURNING id, user_id, condition_type, threshold_value, operator, enabled, description" + ) + .bind(condition_type) + .bind(threshold_value) + .bind(operator) + .bind(enabled) + .bind(description) + .bind(id) + .bind(user_id) + .fetch_one(self.db) + .await + } + + pub async fn delete_threshold(&self, id: i64, user_id: i64) -> Result<(), sqlx::Error> { + sqlx::query("DELETE FROM weather_thresholds WHERE id = ? AND user_id = ?") + .bind(id) + .bind(user_id) + .execute(self.db) + .await?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::users::{UserRepository, UserRole}; + use sqlx::{SqlitePool, Executor}; + use tokio; + + async fn setup_db() -> SqlitePool { + let pool = SqlitePool::connect(":memory:").await.unwrap(); + pool.execute( + "CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id TEXT NOT NULL UNIQUE, + role TEXT NOT NULL DEFAULT 'user' + );" + ).await.unwrap(); + pool.execute( + "CREATE TABLE weather_thresholds ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + condition_type TEXT NOT NULL, + threshold_value REAL NOT NULL, + operator TEXT NOT NULL, + enabled BOOLEAN NOT NULL DEFAULT 1, + description TEXT, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + );" + ).await.unwrap(); + pool + } + + async fn create_user(pool: &SqlitePool) -> i64 { + let repo = UserRepository { db: pool }; + let user = repo.create_user(None, Some(UserRole::User)).await.unwrap(); + user.id + } + + #[tokio::test] + async fn test_create_and_get_threshold() { + let db = setup_db().await; + let user_id = create_user(&db).await; + let repo = WeatherThresholdRepository { db: &db }; + let th = repo.create_threshold(user_id, "wind_speed".to_string(), 10.0, ">".to_string(), true, Some("desc".to_string())).await.unwrap(); + let fetched = repo.get_threshold(th.id, user_id).await.unwrap().unwrap(); + assert_eq!(fetched.condition_type, "wind_speed"); + assert_eq!(fetched.threshold_value, 10.0); + assert_eq!(fetched.operator, ">" + ); + assert_eq!(fetched.enabled, true); + assert_eq!(fetched.description, Some("desc".to_string())); + } + + #[tokio::test] + async fn test_update_threshold() { + let db = setup_db().await; + let user_id = create_user(&db).await; + let repo = WeatherThresholdRepository { db: &db }; + let th = repo.create_threshold(user_id, "wind_speed".to_string(), 10.0, ">".to_string(), true, None).await.unwrap(); + let updated = repo.update_threshold(th.id, user_id, "rain".to_string(), 5.0, "<".to_string(), false, Some("rain desc".to_string())).await.unwrap(); + assert_eq!(updated.condition_type, "rain"); + assert_eq!(updated.threshold_value, 5.0); + assert_eq!(updated.operator, "<"); + assert_eq!(updated.enabled, false); + assert_eq!(updated.description, Some("rain desc".to_string())); + } + + #[tokio::test] + async fn test_delete_threshold() { + let db = setup_db().await; + let user_id = create_user(&db).await; + let repo = WeatherThresholdRepository { db: &db }; + let th = repo.create_threshold(user_id, "wind_speed".to_string(), 10.0, ">".to_string(), true, None).await.unwrap(); + repo.delete_threshold(th.id, user_id).await.unwrap(); + let fetched = repo.get_threshold(th.id, user_id).await.unwrap(); + assert!(fetched.is_none()); + } + + #[tokio::test] + async fn test_list_thresholds() { + let db = setup_db().await; + let user_id = create_user(&db).await; + let repo = WeatherThresholdRepository { db: &db }; + repo.create_threshold(user_id, "wind_speed".to_string(), 10.0, ">".to_string(), true, None).await.unwrap(); + repo.create_threshold(user_id, "rain".to_string(), 5.0, "<".to_string(), false, None).await.unwrap(); + let thresholds = repo.list_thresholds(user_id).await.unwrap(); + assert_eq!(thresholds.len(), 2); + } +} \ No newline at end of file -- cgit v1.2.3