2025-06-03 23:35:34 -04:00
use super ::* ;
use crate ::cache ::Cache ;
2025-06-04 17:21:46 -04:00
use crate ::model ::communities ::{ PollOption , PollVote } ;
2025-06-03 23:35:34 -04:00
use crate ::model ::moderation ::AuditLogEntry ;
use crate ::model ::{ Error , Result , auth ::User , permissions ::FinePermission } ;
use crate ::{ auto_method , execute , get , query_row , params } ;
#[ cfg(feature = " sqlite " ) ]
use rusqlite ::Row ;
use tetratto_shared ::unix_epoch_timestamp ;
#[ cfg(feature = " postgres " ) ]
use tokio_postgres ::Row ;
impl DataManager {
/// Get a [`PollVote`] from an SQL row.
pub ( crate ) fn get_pollvote_from_row (
#[ cfg(feature = " sqlite " ) ] x : & Row < '_ > ,
#[ cfg(feature = " postgres " ) ] x : & Row ,
) -> PollVote {
PollVote {
id : get ! ( x ->0 ( i64 ) ) as usize ,
owner : get ! ( x ->1 ( i64 ) ) as usize ,
created : get ! ( x ->2 ( i64 ) ) as usize ,
poll_id : get ! ( x ->3 ( i64 ) ) as usize ,
vote : ( get! ( x ->4 ( i32 ) ) as u8 ) . into ( ) ,
}
}
auto_method! ( get_pollvote_by_id ( ) @ get_pollvote_from_row -> " SELECT * FROM pollvotes WHERE id = $1 " - - name = " poll vote " - - returns = PollVote - - cache - key - tmpl = " atto.pollvote:{} " ) ;
pub async fn get_pollvote_by_owner_poll ( & self , id : usize , poll_id : usize ) -> Result < PollVote > {
if let Some ( cached ) = self
. 2
. get ( format! ( " atto.pollvote: {} : {} " , id , poll_id ) )
. await
{
return Ok ( serde_json ::from_str ( & cached ) . unwrap ( ) ) ;
}
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = query_row! (
& conn ,
2025-06-04 17:21:46 -04:00
" SELECT * FROM pollvotes WHERE owner = $1 AND poll_id = $2 " ,
2025-06-03 23:35:34 -04:00
& [ & ( id as i64 ) , & ( poll_id as i64 ) ] ,
| x | { Ok ( Self ::get_pollvote_from_row ( x ) ) }
) ;
if res . is_err ( ) {
return Err ( Error ::GeneralNotFound ( " poll vote " . to_string ( ) ) ) ;
}
let x = res . unwrap ( ) ;
self . 2
. set (
format! ( " atto.pollvote: {} : {} " , id , poll_id ) ,
serde_json ::to_string ( & x ) . unwrap ( ) ,
)
. await ;
Ok ( x )
}
/// Create a new poll vote in the database.
///
/// # Arguments
/// * `data` - a mock [`PollVote`] object to insert
pub async fn create_pollvote ( & self , data : PollVote ) -> Result < usize > {
// get poll and check permission
let poll = self . get_poll_by_id ( data . poll_id ) . await ? ;
let now = unix_epoch_timestamp ( ) as usize ;
let diff = now - poll . created ;
if diff > poll . expires {
return Err ( Error ::MiscError ( " Poll is closed " . to_string ( ) ) ) ;
} ;
// ...
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let vote_u8 : u8 = data . vote . into ( ) ;
let res = execute! (
& conn ,
" INSERT INTO pollvotes VALUES ($1, $2, $3, $4, $5) " ,
params! [
& ( data . id as i64 ) ,
& ( data . owner as i64 ) ,
& ( data . created as i64 ) ,
& ( data . poll_id as i64 ) ,
2025-06-04 17:21:46 -04:00
& ( vote_u8 as i32 ) ,
2025-06-03 23:35:34 -04:00
]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
// update poll
2025-06-04 17:21:46 -04:00
match data . vote {
PollOption ::A = > {
self . incr_votes_a_count ( poll . id ) . await ? ;
}
PollOption ::B = > {
self . incr_votes_b_count ( poll . id ) . await ? ;
}
PollOption ::C = > {
self . incr_votes_c_count ( poll . id ) . await ? ;
}
PollOption ::D = > {
self . incr_votes_d_count ( poll . id ) . await ? ;
}
} ;
2025-06-03 23:35:34 -04:00
// ...
Ok ( data . id )
}
pub async fn delete_pollvote ( & self , id : usize , user : User ) -> Result < ( ) > {
let y = self . get_pollvote_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_pollvote` with x value ` {id} ` " ) ,
) )
. await ?
}
}
let conn = match self . connect ( ) . await {
Ok ( c ) = > c ,
Err ( e ) = > return Err ( Error ::DatabaseConnection ( e . to_string ( ) ) ) ,
} ;
let res = execute! (
& conn ,
" DELETE FROM pollvotes WHERE id = $1 " ,
& [ & ( id as i64 ) ]
) ;
if let Err ( e ) = res {
return Err ( Error ::DatabaseError ( e . to_string ( ) ) ) ;
}
self . 2. remove ( format! ( " atto.pollvote: {} " , id ) ) . await ;
Ok ( ( ) )
}
}