#![doc(
html_logo_url = "https://raw.githubusercontent.com/polyphony-chat/design/main/branding/polyphony-chorus-round-8bit.png"
)]
#![allow(clippy::module_inception)]
#![deny(
clippy::extra_unused_lifetimes,
clippy::from_over_into,
clippy::needless_borrow,
clippy::new_without_default
)]
#![warn(
clippy::todo,
clippy::unimplemented,
clippy::dbg_macro,
clippy::print_stdout,
clippy::print_stderr,
missing_debug_implementations,
missing_copy_implementations,
clippy::useless_conversion
)]
#[cfg(all(feature = "rt", feature = "rt_multi_thread"))]
compile_error!("feature \"rt\" and feature \"rt_multi_thread\" cannot be enabled at the same time");
use errors::ChorusResult;
use serde::{Deserialize, Serialize};
use types::types::domains_configuration::WellKnownResponse;
use url::{ParseError, Url};
use crate::errors::ChorusError;
#[cfg(feature = "client")]
pub mod api;
pub mod errors;
#[cfg(feature = "client")]
pub mod gateway;
#[cfg(feature = "client")]
pub mod instance;
#[cfg(feature = "client")]
pub mod ratelimiter;
pub mod types;
#[cfg(all(
feature = "client",
any(feature = "voice_udp", feature = "voice_gateway")
))]
pub mod voice;
#[cfg(not(feature = "sqlx"))]
pub type UInt128 = u128;
#[cfg(feature = "sqlx")]
pub type UInt128 = sqlx_pg_uint::PgU128;
#[cfg(not(feature = "sqlx"))]
pub type UInt64 = u64;
#[cfg(feature = "sqlx")]
pub type UInt64 = sqlx_pg_uint::PgU64;
#[cfg(not(feature = "sqlx"))]
pub type UInt32 = u32;
#[cfg(feature = "sqlx")]
pub type UInt32 = sqlx_pg_uint::PgU32;
#[cfg(not(feature = "sqlx"))]
pub type UInt16 = u16;
#[cfg(feature = "sqlx")]
pub type UInt16 = sqlx_pg_uint::PgU16;
#[cfg(not(feature = "sqlx"))]
pub type UInt8 = u8;
#[cfg(feature = "sqlx")]
pub type UInt8 = sqlx_pg_uint::PgU8;
#[derive(Clone, Default, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct UrlBundle {
pub root: String,
pub api: String,
pub wss: String,
pub cdn: String,
}
impl UrlBundle {
pub fn new(root: &str, api: &str, wss: &str, cdn: &str) -> Self {
Self {
root: UrlBundle::parse_url(root),
api: UrlBundle::parse_url(api),
wss: UrlBundle::parse_url(wss),
cdn: UrlBundle::parse_url(cdn),
}
}
pub fn parse_url(url: &str) -> String {
let url = match Url::parse(url) {
Ok(url) => {
if url.scheme() == "localhost" {
return UrlBundle::parse_url(&format!("http://{}", url));
}
url
}
Err(ParseError::RelativeUrlWithoutBase) => {
let url_fmt = format!("http://{}", url);
return UrlBundle::parse_url(&url_fmt);
}
Err(_) => panic!("Invalid URL"), };
let mut url_string = url.to_string();
if url_string.ends_with('/') {
url_string.pop();
}
url_string
}
pub async fn from_root_url(url: &str) -> ChorusResult<UrlBundle> {
let parsed = UrlBundle::parse_url(url);
let client = reqwest::Client::new();
let request_wellknown = client
.get(format!("{}/.well-known/spacebar", &parsed))
.header(http::header::ACCEPT, "application/json")
.build()?;
let response_wellknown = client.execute(request_wellknown).await?;
if response_wellknown.status().is_success() {
let api_url = response_wellknown.json::<WellKnownResponse>().await?.api;
UrlBundle::from_api_url(&format!("{}/policies/instance/domains", api_url)).await
} else {
if let Ok(response_slash_api) =
UrlBundle::from_api_url(&format!("{}/api/policies/instance/domains", parsed)).await
{
return Ok(response_slash_api);
}
if let Ok(response_api) =
UrlBundle::from_api_url(&format!("{}/policies/instance/domains", parsed)).await
{
Ok(response_api)
} else {
Err(ChorusError::RequestFailed { url: parsed.to_string(), error: "Could not retrieve UrlBundle from url after trying 3 different approaches. Check the provided Url and make sure the instance is reachable.".to_string() } )
}
}
}
async fn from_api_url(url: &str) -> ChorusResult<UrlBundle> {
let client = reqwest::Client::new();
let request = client
.get(url)
.header(http::header::ACCEPT, "application/json")
.build()?;
let response = client.execute(request).await?;
if let Ok(body) = response
.json::<types::types::domains_configuration::Domains>()
.await
{
Ok(UrlBundle::new(
url,
&body.api_endpoint,
&body.gateway,
&body.cdn,
))
} else {
Err(ChorusError::RequestFailed {
url: url.to_string(),
error: "Could not retrieve a UrlBundle from the given url. Check the provided url and make sure the instance is reachable.".to_string(),
})
}
}
}
#[cfg(test)]
mod lib {
use super::*;
#[test]
fn test_parse_url() {
let mut result = UrlBundle::parse_url("localhost:3000/");
assert_eq!(result, "http://localhost:3000");
result = UrlBundle::parse_url("https://some.url.com/");
assert_eq!(result, String::from("https://some.url.com"));
result = UrlBundle::parse_url("https://some.url.com/");
assert_eq!(result, "https://some.url.com");
result = UrlBundle::parse_url("https://some.url.com");
assert_eq!(result, "https://some.url.com");
}
}