2021-07-21 10:23:14 +02:00
use juniper ::{
EmptyMutation ,
EmptySubscription ,
ID ,
RootNode ,
graphql_object ,
GraphQLObject ,
GraphQLEnum ,
2021-07-27 21:11:40 +02:00
FieldResult ,
FieldError ,
2021-07-21 10:23:14 +02:00
} ;
use sqlx ::{
types ::chrono ::{
DateTime ,
Utc ,
} ,
2021-07-27 21:11:40 +02:00
FromRow ,
Encode ,
Decode ,
Type ,
Row ,
Result as SqlxResult ,
Error as SqlxError ,
} ;
#[ cfg(feature = " sqlite " ) ]
use sqlx ::{
SqlitePool ,
sqlite ::SqliteRow ,
2021-07-21 10:23:14 +02:00
} ;
use url ::Url ;
2021-07-27 21:11:40 +02:00
fn into_user_limit (
#[ cfg(feature = " sqlite " ) ]
row : & SqliteRow ,
index : & str
) -> SqlxResult < Option < Vec < String > > > {
let row_value : Option < & str > = row . try_get ( index ) ? ;
if let Some ( s ) = row_value {
into_toml_iter ( s , index ) . map ( | i | Some ( i . collect ( ) ) )
} else {
Ok ( None )
}
}
fn into_server_limit (
#[ cfg(feature = " sqlite " ) ]
row : & SqliteRow ,
index : & str
) -> SqlxResult < Option < Vec < Url > > > {
let row_value : Option < & str > = row . try_get ( index ) ? ;
if let Some ( s ) = row_value {
let mut url_vector = Vec ::new ( ) ;
for s in into_toml_iter ( s , index ) ? {
url_vector . push ( Url ::parse ( s ) . map_err ( into_column_decode_err ( index ) ) ? ) ;
}
Ok ( Some ( url_vector ) )
} else {
Ok ( None )
}
2021-07-21 10:23:14 +02:00
}
2021-07-27 21:11:40 +02:00
#[ inline ]
fn into_column_decode_err < E > ( index : & str ) -> impl FnOnce ( E ) -> SqlxError + '_
where
E : Into < Box < dyn std ::error ::Error + Send + Sync > >
{
move | err : E | SqlxError ::ColumnDecode {
index : index . to_owned ( ) ,
source : err . into ( ) ,
}
}
2021-07-21 10:23:14 +02:00
2021-07-27 21:11:40 +02:00
#[ inline ]
fn into_toml_iter < ' d , T > ( raw : & ' d str , index : & str ) -> SqlxResult < impl Iterator < Item = T > >
where
T : serde ::de ::Deserialize < ' d >
{
Ok ( toml ::from_str ::< Vec < T > > ( raw )
. map_err ( into_column_decode_err ( index ) ) ?
. into_iter ( )
)
}
#[ inline ]
fn into_toml_iter_from_row < ' d , T > (
#[ cfg(feature = " sqlite " ) ]
row : & ' d SqliteRow ,
index : & str
) -> SqlxResult < impl Iterator < Item = T > >
where
T : serde ::de ::Deserialize < ' d >
{
into_toml_iter ( row . try_get ( index ) ? , index )
}
#[ derive(Clone, Debug) ]
pub struct Context {
#[ cfg(feature = " sqlite " ) ]
2021-09-26 16:59:56 +02:00
pub db : SqlitePool ,
2021-07-27 21:11:40 +02:00
}
impl juniper ::Context for Context { }
#[ derive(Clone, Debug, Decode, Type) ]
2021-07-21 10:23:14 +02:00
pub struct User {
id : ID ,
user_name : String ,
display_name : Option < String > ,
activated : bool ,
created : DateTime < Utc > ,
last_online : Option < DateTime < Utc > > ,
preferences : UserPreferences ,
}
2021-07-27 21:11:40 +02:00
#[ graphql_object(context = Context) ]
impl User {
pub const fn id ( & self ) -> & ID {
& self . id
}
pub const fn user_name ( & self ) -> & String {
& self . user_name
}
pub const fn display_name ( & self ) -> Option < & String > {
self . display_name . as_ref ( )
}
pub const fn activated ( & self ) -> bool {
self . activated
}
pub const fn created ( & self ) -> & DateTime < Utc > {
& self . created
}
pub const fn last_online ( & self ) -> Option < & DateTime < Utc > > {
self . last_online . as_ref ( )
}
pub async fn preferences ( & self , context : & Context ) -> FieldResult < UserPreferences > {
UserPreferences ::try_get ( & self . id , & context . db ) . await . map_err ( FieldError ::from )
}
}
impl User {
async fn from_row_with_db (
#[ cfg(feature = " sqlite " ) ]
row : & SqliteRow ,
#[ cfg(feature = " sqlite " ) ]
db : & SqlitePool ,
) -> Result < Self , SqlxError > {
let id = row . try_get ::< String , _ > ( " id " ) ? . into ( ) ;
Ok ( User {
user_name : row . try_get ( " user_name " ) ? ,
display_name : row . try_get ( " display_name " ) ? ,
activated : row . try_get ( " activated " ) ? ,
created : row . try_get ( " created " ) ? ,
last_online : row . try_get ( " last_online " ) ? ,
preferences : UserPreferences ::try_get ( & id , db ) . await ? ,
id ,
} )
}
}
#[ derive(Clone, Debug, GraphQLObject, Decode, Encode, FromRow) ]
2021-07-21 10:23:14 +02:00
pub struct UserPreferences {
privacy_preferences : PrivacyPreferences ,
notification_preferences : NotificationPreferences ,
security_preferences : SecurityPreferences ,
external_servers_preferences : ExternalServersPreferences ,
}
2021-07-27 21:11:40 +02:00
impl UserPreferences {
async fn try_get ( id : & ID , db : & SqlitePool ) -> SqlxResult < Self > {
let privacy_preferences : PrivacyPreferences =
sqlx ::query_as ( " SELECT * FROM privacy_preferences WHERE users.id = privacy_preferences.id " )
. fetch_one ( db ) . await ? ;
let notification_preferences : NotificationPreferences =
sqlx ::query_as ( " SELECT * FROM notification_preferences WHERE users.id = privacy_preferences.id " )
. fetch_one ( db ) . await ? ;
let security_preferences : SecurityPreferences =
sqlx ::query_as ( " SELECT * FROM security_preferences WHERE users.id = privacy_preferences.id " )
. fetch_one ( db ) . await ? ;
let external_servers_preferences = ExternalServersPreferences ::from_row_with_db (
sqlx ::query ( & * format! ( " SELECT * FROM external_server_preferences WHERE id = {} " , id ) )
. fetch_one ( db ) . await ? ,
db ,
) . await ? ;
Ok ( UserPreferences {
privacy_preferences ,
notification_preferences ,
security_preferences ,
external_servers_preferences ,
} )
}
}
2021-07-21 10:23:14 +02:00
#[ derive(Clone, Debug, GraphQLObject) ]
pub struct PrivacyPreferences {
discovery : RestrictionPolicy ,
discovery_user_limit : Option < Vec < String > > ,
discovery_server_limit : Option < Vec < Url > > ,
last_seen : RestrictionPolicy ,
last_seen_user_limit : Option < Vec < String > > ,
last_seen_server_limit : Option < Vec < Url > > ,
last_seen_course : bool ,
info : RestrictionPolicy ,
info_user_limit : Option < Vec < String > > ,
info_server_limit : Option < Vec < Url > > ,
}
2021-07-27 21:11:40 +02:00
impl PrivacyPreferences {
fn from_row_cfg (
#[ cfg(feature = " sqlite " ) ]
row : & SqliteRow ,
) -> SqlxResult < Self > {
Ok ( PrivacyPreferences {
discovery : row . try_get ( " discovery " ) ? ,
discovery_user_limit : into_user_limit ( row , " discovery_user_limit " ) ? ,
discovery_server_limit : into_server_limit ( row , " discovery_server_limit " ) ? ,
last_seen : row . try_get ( " last_seen " ) ? ,
last_seen_user_limit : into_user_limit ( row , " last_seen_user_limit " ) ? ,
last_seen_server_limit : into_server_limit ( row , " last_seen_server_limit " ) ? ,
last_seen_course : row . try_get ( " last_seen_course " ) ? ,
info : row . try_get ( " info " ) ? ,
info_user_limit : into_user_limit ( row , " info_user_limit " ) ? ,
info_server_limit : into_server_limit ( row , " info_server_limit " ) ? ,
} )
}
}
#[ cfg(feature = " sqlite " ) ]
impl FromRow < '_ , SqliteRow > for PrivacyPreferences {
fn from_row ( row : & SqliteRow ) -> SqlxResult < Self > {
Self ::from_row_cfg ( row )
}
}
#[ derive(Clone, Copy, Debug, GraphQLObject, Decode, Encode, FromRow) ]
2021-07-21 10:23:14 +02:00
pub struct NotificationPreferences {
lock_details : bool ,
do_not_disturb : bool ,
}
2021-07-27 21:11:40 +02:00
#[ derive(Clone, Debug, GraphQLObject, Decode, Encode) ]
2021-07-21 10:23:14 +02:00
pub struct SecurityPreferences {
account_tokens : Vec < ID > ,
password_hash : String ,
}
2021-07-27 21:11:40 +02:00
impl SecurityPreferences {
fn from_row_cfg (
#[ cfg(feature = " sqlite " ) ]
row : & SqliteRow ,
) -> SqlxResult < Self > {
Ok ( SecurityPreferences {
account_tokens : into_toml_iter_from_row ::< & str > ( row , " account_tokens " ) ?
. map ( ID ::new )
. collect ( ) ,
password_hash : row . try_get ( " password_hash " ) ? ,
} )
}
}
#[ cfg(feature = " sqlite " ) ]
impl FromRow < '_ , SqliteRow > for SecurityPreferences {
fn from_row ( row : & SqliteRow ) -> SqlxResult < Self > {
Self ::from_row_cfg ( row )
}
}
#[ derive(Clone, Debug, GraphQLObject, Decode, Encode, FromRow) ]
2021-07-21 10:23:14 +02:00
pub struct ExternalServersPreferences {
privacy_preferences : PrivacyPreferences ,
external_servers : RestrictionPolicy ,
external_servers_limit : Option < Vec < Url > > ,
}
2021-07-27 21:11:40 +02:00
impl ExternalServersPreferences {
async fn from_row_with_db (
#[ cfg(feature = " sqlite " ) ]
row : SqliteRow ,
#[ cfg(feature = " sqlite " ) ]
db : & SqlitePool ,
) -> SqlxResult < Self > {
Ok ( Self {
privacy_preferences :
sqlx ::query_as ( & * format! ( " SELECT * FROM external_server_privacy_preferences WHERE external_servers_preferences.id = external_servers_privacy_preferences.id && id = {} " , row . try_get ::< String , _ > ( " id " ) ? ) )
. fetch_one ( db ) . await ? ,
external_servers : row . try_get ( " external_servers " ) ? ,
external_servers_limit : into_server_limit ( & row , " external_server_limit " ) ? ,
} )
}
}
#[ derive(Clone, Copy, Debug, GraphQLEnum, Type) ]
2021-07-21 10:23:14 +02:00
pub enum RestrictionPolicy {
2021-07-27 21:11:40 +02:00
Everyone = 0 ,
Excluding = 1 ,
Including = 2 ,
None = 3 ,
2021-07-21 10:23:14 +02:00
}
#[ derive(Clone, Copy, Debug) ]
2021-07-27 21:11:40 +02:00
pub struct Query ;
impl Query {
async fn users_sqlx_result ( context : & Context ) -> SqlxResult < Vec < User > > {
let rows = sqlx ::query ( " SELECT * FROM users " )
. fetch_all ( & context . db ) . await ? ;
let mut users = Vec ::with_capacity ( rows . capacity ( ) ) ;
for row in rows {
users . push ( User ::from_row_with_db ( & row , & context . db ) . await ? ) ;
}
Ok ( users )
}
2021-07-21 10:23:14 +02:00
}
2021-07-27 21:11:40 +02:00
#[ graphql_object(context = Context) ]
impl Query {
async fn users ( context : & Context ) -> FieldResult < Vec < User > > {
Self ::users_sqlx_result ( context ) . await . map_err ( FieldError ::from )
}
async fn user_preferences ( context : & Context , id : ID ) -> FieldResult < UserPreferences > {
UserPreferences ::try_get ( & id , & context . db ) . await . map_err ( FieldError ::from )
2021-07-21 10:23:14 +02:00
}
}
2021-07-27 21:11:40 +02:00
pub type Schema < ' root_node > = RootNode < ' root_node , Query , EmptyMutation < Context > , EmptySubscription < Context > > ;
2021-07-21 10:23:14 +02:00
2021-07-27 21:11:40 +02:00
pub fn schema < ' root_node > ( ) -> Schema < ' root_node > {
Schema ::new ( Query , EmptyMutation ::new ( ) , EmptySubscription ::new ( ) )
2021-07-21 10:23:14 +02:00
}