use anyhow::Context; use sqlx::FromRow; use crate::domain::warren::{ models::option::{ DeleteOptionError, DeleteOptionRequest, DeleteOptionResponse, GetOptionError, GetOptionRequest, GetOptionResponse, OptionKey, OptionType, SetOptionError, SetOptionRequest, SetOptionResponse, }, ports::OptionRepository, }; use super::{Sqlite, is_not_found_error}; #[derive(Debug, FromRow)] struct OptionRow { key: String, value: String, } impl OptionRepository for Sqlite { async fn get_option( &self, request: GetOptionRequest, ) -> Result, GetOptionError> { let mut connection = self .pool .acquire() .await .context("Failed to get a Sqlite connection")?; let key: OptionKey = request.into(); let row: OptionRow = sqlx::query_as( " SELECT key, value FROM application_options WHERE key = $1", ) .bind(key.as_str()) .fetch_one(&mut *connection) .await .map_err(|e| { if is_not_found_error(&e) { GetOptionError::NotFound(key) } else { GetOptionError::Unknown(e.into()) } })?; let parsed = T::parse(&row.value).map_err(|_| GetOptionError::Parse)?; Ok(GetOptionResponse::new( OptionKey::new(&row.key).unwrap(), parsed, )) } async fn set_option( &self, request: SetOptionRequest, ) -> Result, SetOptionError> { let mut connection = self .pool .acquire() .await .context("Failed to get a Sqlite connection")?; let (key, value) = request.unpack(); sqlx::query_as::<_, OptionRow>( " INSERT INTO application_options ( key, value ) VALUES ( $1, $2 ) RETURNING key, value ", ) .bind(key.as_str()) .bind(value.inner().to_string()) .fetch_one(&mut *connection) .await .map_err(|e| SetOptionError::Unknown(e.into()))?; Ok(SetOptionResponse::new(key, value.get_inner())) } async fn delete_option( &self, request: DeleteOptionRequest, ) -> Result { let mut connection = self .pool .acquire() .await .context("Failed to get a Sqlite connection")?; let key: OptionKey = request.into(); sqlx::query_as::<_, OptionRow>( " DELETE FROM application_options WHERE key = $1 RETURNING key, value ", ) .bind(key.as_str()) .fetch_one(&mut *connection) .await .map_err(|e| { if is_not_found_error(&e) { DeleteOptionError::NotFound(key.clone()) } else { DeleteOptionError::Unknown(e.into()) } })?; Ok(DeleteOptionResponse::new(key)) } }