2023-08-26 19:41:00 +02:00
//! Instance and ChorusUser objects.
2023-07-09 18:38:02 +02:00
use std ::collections ::HashMap ;
2023-06-11 13:52:31 +02:00
use std ::fmt ;
2023-08-28 18:12:17 +02:00
2023-08-12 19:31:31 +02:00
use std ::sync ::{ Arc , RwLock } ;
2023-06-11 13:52:31 +02:00
2023-06-20 22:41:31 +02:00
use reqwest ::Client ;
2023-05-26 12:50:16 +02:00
use serde ::{ Deserialize , Serialize } ;
2023-07-09 18:38:02 +02:00
use crate ::errors ::ChorusResult ;
2023-11-19 19:12:29 +01:00
use crate ::gateway ::{ Gateway , GatewayHandle } ;
2023-07-09 18:38:02 +02:00
use crate ::ratelimiter ::ChorusRequest ;
use crate ::types ::types ::subconfigs ::limits ::rates ::RateLimits ;
2023-12-03 21:44:08 +01:00
use crate ::types ::{
GeneralConfiguration , Limit , LimitType , LimitsConfiguration , User , UserSettings ,
} ;
2023-11-19 19:12:29 +01:00
use crate ::UrlBundle ;
2023-04-16 22:16:22 +02:00
2023-12-02 17:35:47 +01:00
#[ derive(Debug, Clone, Default, Serialize, Deserialize) ]
2023-07-10 16:59:00 +02:00
/// The [`Instance`]; what you will be using to perform all sorts of actions on the Spacebar server.
/// If `limits_information` is `None`, then the instance will not be rate limited.
2023-05-07 12:39:04 +02:00
pub struct Instance {
2023-06-20 02:59:18 +02:00
pub urls : UrlBundle ,
2023-05-25 23:09:18 +02:00
pub instance_info : GeneralConfiguration ,
2023-07-09 18:38:02 +02:00
pub limits_information : Option < LimitsInformation > ,
2023-12-02 17:35:47 +01:00
#[ serde(skip) ]
2023-06-20 22:41:31 +02:00
pub client : Client ,
2023-04-16 23:03:12 +02:00
}
2023-12-02 17:35:47 +01:00
impl PartialEq for Instance {
fn eq ( & self , other : & Self ) -> bool {
self . urls = = other . urls
& & self . instance_info = = other . instance_info
& & self . limits_information = = other . limits_information
}
}
impl Eq for Instance { }
impl std ::hash ::Hash for Instance {
fn hash < H : std ::hash ::Hasher > ( & self , state : & mut H ) {
self . urls . hash ( state ) ;
self . instance_info . hash ( state ) ;
if let Some ( inf ) = & self . limits_information {
inf . hash ( state ) ;
}
}
}
#[ derive(Debug, Clone, Serialize, Deserialize, Default, Eq) ]
2023-07-09 18:38:02 +02:00
pub struct LimitsInformation {
pub ratelimits : HashMap < LimitType , Limit > ,
pub configuration : RateLimits ,
}
2023-12-02 17:35:47 +01:00
impl std ::hash ::Hash for LimitsInformation {
fn hash < H : std ::hash ::Hasher > ( & self , state : & mut H ) {
for ( k , v ) in self . ratelimits . iter ( ) {
k . hash ( state ) ;
v . hash ( state ) ;
}
self . configuration . hash ( state ) ;
}
}
impl PartialEq for LimitsInformation {
fn eq ( & self , other : & Self ) -> bool {
self . ratelimits . iter ( ) . eq ( other . ratelimits . iter ( ) )
& & self . configuration = = other . configuration
}
}
2023-05-07 12:39:04 +02:00
impl Instance {
2023-12-10 18:40:03 +01:00
/// Creates a new [`Instance`] from the [relevant instance urls](UrlBundle). To create an Instance from one singular url, use [`Instance::from_root_url()`].
2023-12-03 21:44:08 +01:00
pub async fn new ( urls : UrlBundle ) -> ChorusResult < Instance > {
let is_limited : Option < LimitsConfiguration > = Instance ::is_limited ( & urls . api ) . await ? ;
let limit_information ;
if let Some ( limits_configuration ) = is_limited {
let limits = ChorusRequest ::limits_config_to_hashmap ( & limits_configuration . rate ) ;
limit_information = Some ( LimitsInformation {
2023-11-15 20:26:47 +01:00
ratelimits : limits ,
2023-12-03 21:44:08 +01:00
configuration : limits_configuration . rate ,
2023-07-09 18:38:02 +02:00
} ) ;
} else {
2023-12-03 21:44:08 +01:00
limit_information = None
2023-07-09 18:38:02 +02:00
}
2023-04-19 20:41:33 +02:00
let mut instance = Instance {
2023-04-24 19:49:26 +02:00
urls : urls . clone ( ) ,
2023-06-19 10:27:32 +02:00
// Will be overwritten in the next step
instance_info : GeneralConfiguration ::default ( ) ,
2023-12-03 21:44:08 +01:00
limits_information : limit_information ,
2023-06-20 22:41:31 +02:00
client : Client ::new ( ) ,
2023-04-19 20:41:33 +02:00
} ;
2023-05-25 23:09:18 +02:00
instance . instance_info = match instance . general_configuration_schema ( ) . await {
2023-04-19 20:41:33 +02:00
Ok ( schema ) = > schema ,
2023-04-21 23:20:23 +02:00
Err ( e ) = > {
2023-07-09 18:38:02 +02:00
log ::warn! ( " Could not get instance configuration schema: {} " , e ) ;
GeneralConfiguration ::default ( )
2023-04-21 23:20:23 +02:00
}
2023-04-19 20:41:33 +02:00
} ;
Ok ( instance )
}
2023-12-03 13:37:32 +01:00
2023-07-09 18:38:02 +02:00
pub ( crate ) fn clone_limits_if_some ( & self ) -> Option < HashMap < LimitType , Limit > > {
if self . limits_information . is_some ( ) {
return Some ( self . limits_information . as_ref ( ) . unwrap ( ) . ratelimits . clone ( ) ) ;
}
None
}
2023-12-03 13:37:32 +01:00
2023-12-03 13:39:23 +01:00
/// Creates a new [`Instance`] by trying to get the [relevant instance urls](UrlBundle) from a root url.
2023-12-03 13:37:32 +01:00
/// Shorthand for `Instance::new(UrlBundle::from_root_domain(root_domain).await?)`.
///
/// If `limited` is `true`, then Chorus will track and enforce rate limits for this instance.
2023-12-03 21:44:08 +01:00
pub async fn from_root_url ( root_url : & str ) -> ChorusResult < Instance > {
2023-12-03 13:39:23 +01:00
let urls = UrlBundle ::from_root_url ( root_url ) . await ? ;
2023-12-03 21:44:08 +01:00
Instance ::new ( urls ) . await
}
pub async fn is_limited ( api_url : & str ) -> ChorusResult < Option < LimitsConfiguration > > {
let api_url = UrlBundle ::parse_url ( api_url . to_string ( ) ) ;
let client = Client ::new ( ) ;
let request = client
. get ( format! ( " {} /policies/instance/limits " , & api_url ) )
. header ( http ::header ::ACCEPT , " application/json " )
. build ( ) ? ;
let resp = match client . execute ( request ) . await {
Ok ( response ) = > response ,
Err ( _ ) = > return Ok ( None ) ,
} ;
match resp . json ::< LimitsConfiguration > ( ) . await {
Ok ( limits ) = > Ok ( Some ( limits ) ) ,
Err ( _ ) = > Ok ( None ) ,
}
2023-12-03 13:37:32 +01:00
}
2023-04-19 20:41:33 +02:00
}
2023-05-26 12:50:16 +02:00
#[ derive(Debug, PartialEq, Eq, Serialize, Deserialize) ]
2023-04-21 23:20:23 +02:00
pub struct Token {
pub token : String ,
2023-04-19 20:41:33 +02:00
}
2023-04-21 23:20:23 +02:00
impl fmt ::Display for Token {
2023-04-19 20:41:33 +02:00
fn fmt ( & self , f : & mut fmt ::Formatter ) -> fmt ::Result {
2023-04-21 23:20:23 +02:00
write! ( f , " {} " , self . token )
2023-04-19 20:41:33 +02:00
}
2023-04-16 22:16:22 +02:00
}
2023-04-17 21:31:15 +02:00
2023-08-29 00:05:16 +02:00
#[ derive(Debug, Clone) ]
2023-08-24 21:09:23 +02:00
/// A ChorusUser is a representation of an authenticated user on an [Instance].
2023-07-29 10:23:04 +02:00
/// It is used for most authenticated actions on a Spacebar server.
/// It also has its own [Gateway] connection.
2023-08-24 21:06:28 +02:00
pub struct ChorusUser {
2023-08-28 18:12:17 +02:00
pub belongs_to : Arc < RwLock < Instance > > ,
2023-05-25 23:09:18 +02:00
pub token : String ,
2023-07-09 18:38:02 +02:00
pub limits : Option < HashMap < LimitType , Limit > > ,
2023-08-12 19:31:31 +02:00
pub settings : Arc < RwLock < UserSettings > > ,
pub object : Arc < RwLock < User > > ,
2023-08-29 14:24:32 +02:00
pub gateway : GatewayHandle ,
2023-05-25 23:09:18 +02:00
}
2023-12-02 20:30:04 +01:00
impl PartialEq for ChorusUser {
fn eq ( & self , other : & Self ) -> bool {
self . token = = other . token
& & self . limits = = other . limits
& & self . gateway . url = = other . gateway . url
}
}
impl Eq for ChorusUser { }
2023-08-24 21:06:28 +02:00
impl ChorusUser {
2023-05-25 23:09:18 +02:00
pub fn token ( & self ) -> String {
self . token . clone ( )
}
pub fn set_token ( & mut self , token : String ) {
self . token = token ;
}
2023-08-24 21:09:23 +02:00
/// Creates a new [ChorusUser] from existing data.
2023-07-29 10:23:04 +02:00
///
/// # Notes
2023-08-24 21:09:23 +02:00
/// This isn't the prefered way to create a ChorusUser.
2023-07-29 10:23:04 +02:00
/// See [Instance::login_account] and [Instance::register_account] instead.
2023-05-25 23:09:18 +02:00
pub fn new (
2023-08-28 18:12:17 +02:00
belongs_to : Arc < RwLock < Instance > > ,
2023-05-25 23:09:18 +02:00
token : String ,
2023-07-09 18:38:02 +02:00
limits : Option < HashMap < LimitType , Limit > > ,
2023-08-12 19:31:31 +02:00
settings : Arc < RwLock < UserSettings > > ,
object : Arc < RwLock < User > > ,
2023-08-29 14:24:32 +02:00
gateway : GatewayHandle ,
2023-08-24 21:06:28 +02:00
) -> ChorusUser {
ChorusUser {
2023-05-25 23:09:18 +02:00
belongs_to ,
token ,
limits ,
settings ,
object ,
2023-07-24 19:13:53 +02:00
gateway ,
2023-05-25 23:09:18 +02:00
}
}
2023-07-09 18:38:02 +02:00
/// Creates a new 'shell' of a user. The user does not exist as an object, and exists so that you have
2023-08-24 21:09:23 +02:00
/// a ChorusUser object to make Rate Limited requests with. This is useful in scenarios like
2023-07-09 18:38:02 +02:00
/// registering or logging in to the Instance, where you do not yet have a User object, but still
2023-07-24 19:13:53 +02:00
/// need to make a RateLimited request. To use the [`GatewayHandle`], you will have to identify
/// first.
2023-08-28 18:12:17 +02:00
pub ( crate ) async fn shell ( instance : Arc < RwLock < Instance > > , token : String ) -> ChorusUser {
2023-08-12 19:31:31 +02:00
let settings = Arc ::new ( RwLock ::new ( UserSettings ::default ( ) ) ) ;
let object = Arc ::new ( RwLock ::new ( User ::default ( ) ) ) ;
2023-08-28 18:12:17 +02:00
let wss_url = instance . read ( ) . unwrap ( ) . urls . wss . clone ( ) ;
2023-07-24 19:13:53 +02:00
// Dummy gateway object
2023-11-18 19:07:56 +01:00
let gateway = Gateway ::spawn ( wss_url ) . await . unwrap ( ) ;
2023-08-24 21:06:28 +02:00
ChorusUser {
2023-07-09 18:38:02 +02:00
token ,
2023-07-24 19:13:53 +02:00
belongs_to : instance . clone ( ) ,
2023-07-09 18:38:02 +02:00
limits : instance
2023-08-28 18:12:17 +02:00
. read ( )
. unwrap ( )
2023-07-09 18:38:02 +02:00
. limits_information
. as_ref ( )
. map ( | info | info . ratelimits . clone ( ) ) ,
settings ,
object ,
2023-07-24 19:13:53 +02:00
gateway ,
2023-07-09 18:38:02 +02:00
}
}
2023-05-25 23:09:18 +02:00
}