use uuid::Uuid; use serde::Serialize; use sqlx::{PgPool, postgres::PgQueryResult}; use crate::app_error::AppError; #[derive(Serialize, sqlx::FromRow)] pub struct Device { pub id: Uuid, pub home_id: Uuid, pub name: String, pub description: Option, pub device_type: DeviceType } #[derive(Serialize, sqlx::Type)] #[sqlx(type_name = "device_type", rename_all = "snake_case")] pub enum DeviceType { OnOffSwitch } impl Device { pub fn new( home_id: Uuid, name: String, description: Option, device_type: String ) -> Result { Ok(Self { id: Uuid::now_v7(), home_id: home_id, name: name, description: description, device_type: match device_type.as_str() { "on_off_switch" => DeviceType::OnOffSwitch, _ => {return Err(AppError::invalid_input("Invalid device type"))} } }) } pub async fn insert( &self, db: &PgPool, user_id: Uuid, home_id: Uuid ) -> Result<(), AppError> { let result: PgQueryResult = sqlx::query!( "INSERT INTO devices (id, home_id, name, description, device_type) SELECT $1, $2, $3, $4, $5 WHERE EXISTS ( SELECT 1 FROM users_homes WHERE user_id = $6 AND home_id = $2 AND is_admin = true )", self.id, home_id, self.name, self.description, &self.device_type as &DeviceType, user_id ) .execute(db) .await?; if result.rows_affected() == 0 { return Err(AppError::forbidden("Unauthorized access to home")); } Ok(()) } pub async fn delete( db: &PgPool, user_id: Uuid, home_id: Uuid, device_id: Uuid ) -> Result<(), AppError> { let result = sqlx::query!( "DELETE FROM devices WHERE id = $1 AND home_id = $2 AND EXISTS ( SELECT 1 FROM users_homes WHERE user_id = $3 AND home_id = $2 AND is_admin = true );", device_id, home_id, user_id ) .execute(db) .await?; if result.rows_affected() == 0 { return Err(AppError::forbidden("Unauthorized access to device")); } Ok(()) } pub async fn find_one( db: &PgPool, user_id: Uuid, home_id: Uuid, device_id: Uuid, ) -> Result { let device = sqlx::query_as::<_, Device>( "SELECT devices.* FROM devices JOIN users_homes ON users_homes.home_id = devices.home_id WHERE devices.id = $1 AND devices.home_id = $2 AND users_homes.user_id = $3" ) .bind(device_id) .bind(home_id) .bind(user_id) .fetch_optional(db) .await?; match device { Some(d) => Ok(d), None => Err(AppError::forbidden("Unauthorized access to device")) } } }