2025-06-08 14:15:42 -04:00
use oiseau ::cache ::Cache ;
2025-06-03 23:35:34 -04:00
use crate ::model ::communities ::Poll ;
use crate ::model ::moderation ::AuditLogEntry ;
use crate ::model ::{ Error , Result , auth ::User , permissions ::FinePermission } ;
2025-06-08 14:15:42 -04:00
use crate ::{ auto_method , DataManager } ;
2025-06-03 23:35:34 -04:00
#[ cfg(feature = " sqlite " ) ]
2025-06-08 14:15:42 -04:00
use oiseau ::SqliteRow ;
2025-06-03 23:35:34 -04:00
#[ cfg(feature = " postgres " ) ]
2025-06-08 14:15:42 -04:00
use oiseau ::PostgresRow ;
use oiseau ::{ execute , get , query_rows , params } ;
2025-06-03 23:35:34 -04:00
impl DataManager {
/// Get a [`Poll`] from an SQL row.
pub ( crate ) fn get_poll_from_row (
2025-06-08 14:15:42 -04:00
#[ cfg(feature = " sqlite " ) ] x : & SqliteRow < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & PostgresRow ,
2025-06-03 23:35:34 -04:00
) -> Poll {
Poll {
id : get ! ( x ->0 ( i64 ) ) as usize ,
owner : get ! ( x ->1 ( i64 ) ) as usize ,
created : get ! ( x ->2 ( i64 ) ) as usize ,
2025-06-04 17:21:46 -04:00
expires : get ! ( x ->3 ( i32 ) ) as usize ,
2025-06-03 23:35:34 -04:00
option_a : get ! ( x ->4 ( String ) ) ,
option_b : get ! ( x ->5 ( String ) ) ,
option_c : get ! ( x ->6 ( String ) ) ,
option_d : get ! ( x ->7 ( String ) ) ,
votes_a : get ! ( x ->8 ( i32 ) ) as usize ,
votes_b : get ! ( x ->9 ( i32 ) ) as usize ,
votes_c : get ! ( x ->10 ( i32 ) ) as usize ,
votes_d : get ! ( x ->11 ( i32 ) ) as usize ,
}
}
auto_method! ( get_poll_by_id ( ) @ get_poll_from_row -> " SELECT * FROM polls WHERE id = $1 " - - name = " poll " - - returns = Poll - - cache - key - tmpl = " atto.poll:{} " ) ;
2025-06-07 21:18:28 -04:00
/// Get all polls by their owner.
///
/// # Arguments
/// * `owner` - the ID of the owner of the polls
pub async fn get_polls_by_owner_all ( & self , owner : usize ) -> Result < Vec < Poll > > {
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-06-07 21:18:28 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_rows! (
& conn ,
" SELECT * FROM polls WHERE owner = $1 ORDER BY created DESC " ,
& [ & ( owner as i64 ) ] ,
| x | { Self ::get_poll_from_row ( x ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " poll " . to_string ( ) ) ) ;
}
Ok ( res . unwrap ( ) )
}
2025-06-03 23:35:34 -04:00
/// Create a new poll in the database.
///
/// # Arguments
/// * `data` - a mock [`Poll`] object to insert
pub async fn create_poll ( & self , data : Poll ) -> Result < usize > {
// check values
if data . option_a . len ( ) < 2 {
return Err ( Error ::DataTooShort ( " option A " . to_string ( ) ) ) ;
} else if data . option_a . len ( ) > 128 {
return Err ( Error ::DataTooLong ( " option A " . to_string ( ) ) ) ;
}
if data . option_b . len ( ) < 2 {
return Err ( Error ::DataTooShort ( " option B " . to_string ( ) ) ) ;
} else if data . option_b . len ( ) > 128 {
return Err ( Error ::DataTooLong ( " option B " . to_string ( ) ) ) ;
}
if data . option_c . len ( ) > 128 {
return Err ( Error ::DataTooLong ( " option C " . to_string ( ) ) ) ;
}
if data . option_d . len ( ) > 128 {
return Err ( Error ::DataTooLong ( " option D " . to_string ( ) ) ) ;
}
// ...
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-06-03 23:35:34 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" INSERT INTO polls VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12) " ,
params! [
& ( data . id as i64 ) ,
& ( data . owner as i64 ) ,
& ( data . created as i64 ) ,
2025-06-04 17:21:46 -04:00
& ( data . expires as i32 ) ,
2025-06-03 23:35:34 -04:00
& data . option_a ,
& data . option_b ,
& data . option_c ,
& data . option_d ,
2025-06-04 17:21:46 -04:00
& ( data . votes_a as i32 ) ,
& ( data . votes_b as i32 ) ,
& ( data . votes_c as i32 ) ,
& ( data . votes_d as i32 ) ,
2025-06-03 23:35:34 -04:00
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
Ok ( data . id )
}
2025-06-07 21:18:28 -04:00
pub async fn delete_poll ( & self , id : usize , user : & User ) -> Result < ( ) > {
2025-06-03 23:35:34 -04:00
let y = self . get_poll_by_id ( id ) . await ? ;
if user . id ! = y . owner {
if ! user . permissions . check ( FinePermission ::MANAGE_POSTS ) {
return Err ( Error ::NotAllowed ) ;
} else {
self . create_audit_log_entry ( AuditLogEntry ::new (
user . id ,
format! ( " invoked `delete_poll` with x value ` {id} ` " ) ,
) )
. await ?
}
}
2025-06-08 14:15:42 -04:00
let conn = match self . 0. connect ( ) . await {
2025-06-03 23:35:34 -04:00
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! ( & conn , " DELETE FROM polls WHERE id = $1 " , & [ & ( id as i64 ) ] ) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
2025-06-08 14:15:42 -04:00
self . 0. 1. remove ( format! ( " atto.poll: {} " , id ) ) . await ;
2025-06-03 23:35:34 -04:00
2025-06-04 17:21:46 -04:00
// remove votes
let res = execute! (
& conn ,
" DELETE FROM pollvotes WHERE poll_id = $1 " ,
& [ & ( id as i64 ) ]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
// ...
2025-06-03 23:35:34 -04:00
Ok ( ( ) )
}
pub async fn cache_clear_poll ( & self , poll : & Poll ) {
2025-06-08 14:15:42 -04:00
self . 0. 1. remove ( format! ( " atto.poll: {} " , poll . id ) ) . await ;
2025-06-03 23:35:34 -04:00
}
2025-06-04 17:21:46 -04:00
auto_method! ( incr_votes_a_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_a = votes_a + 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - incr ) ;
auto_method! ( decr_votes_a_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_a = votes_a - 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - decr = votes_a ) ;
2025-06-03 23:35:34 -04:00
2025-06-04 17:21:46 -04:00
auto_method! ( incr_votes_b_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_b = votes_b + 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - incr ) ;
auto_method! ( decr_votes_b_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_b = votes_b - 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - decr = votes_b ) ;
2025-06-03 23:35:34 -04:00
2025-06-04 17:21:46 -04:00
auto_method! ( incr_votes_c_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_c = votes_d + 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - incr ) ;
auto_method! ( decr_votes_c_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_c = votes_d - 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - decr = votes_c ) ;
2025-06-03 23:35:34 -04:00
2025-06-04 17:21:46 -04:00
auto_method! ( incr_votes_d_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_d = votes_d + 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - incr ) ;
auto_method! ( decr_votes_d_count ( ) @ get_poll_by_id -> " UPDATE polls SET votes_d = votes_d - 1 WHERE id = $1 " - - cache - key - tmpl = cache_clear_poll - - decr = votes_d ) ;
2025-06-03 23:35:34 -04:00
}