use std::collections::HashMap;
use crate::caching::TokenSourceCached;
use crate::error::TokenSourceError;
use crate::request::{TokenSourceFetchOptions, TokenSourceRequest};
use crate::response::{TokenSourceResponse, TokenSourceResult};
use async_trait::async_trait;
use livekit_net::{Header, HttpClientExt};
const DEVELOPMENT_TOKEN_SERVER_ENDPOINT_URL: &str =
"https://cloud-api.livekit.io/api/v2/sandbox/connection-details";
const DEVELOPMENT_TOKEN_SERVER_ID_HEADER: &str = "X-Sandbox-ID";
#[async_trait]
pub trait TokenSourceFixed {
async fn fetch(&self) -> TokenSourceResult<TokenSourceResponse>;
}
pub struct TokenSourceLiteral {
response: TokenSourceResponse,
}
#[async_trait]
impl TokenSourceFixed for TokenSourceLiteral {
async fn fetch(&self) -> TokenSourceResult<TokenSourceResponse> {
Ok(self.response.clone())
}
}
pub fn literal(
server_url: impl Into<String>,
participant_token: impl Into<String>,
) -> TokenSourceLiteral {
TokenSourceLiteral {
response: TokenSourceResponse {
server_url: server_url.into(),
participant_token: participant_token.into(),
},
}
}
#[async_trait]
pub trait TokenSourceConfigurable {
async fn fetch(
&self,
options: &TokenSourceFetchOptions,
) -> TokenSourceResult<TokenSourceResponse>;
fn cached(self) -> TokenSourceCached<Self>
where
Self: Sized + Send + Sync,
{
TokenSourceCached::new(self)
}
}
pub struct TokenSourceEndpoint {
endpoint_url: String,
headers: HashMap<String, String>,
}
#[async_trait]
impl TokenSourceConfigurable for TokenSourceEndpoint {
async fn fetch(
&self,
options: &TokenSourceFetchOptions,
) -> TokenSourceResult<TokenSourceResponse> {
let request = TokenSourceRequest::from(options);
let http_client =
livekit_net::http_client().ok_or(TokenSourceError::TransportNotConfigured)?;
let body = serde_json::to_vec(&request)?;
let mut headers =
vec![Header { name: "Content-Type".into(), value: "application/json".into() }];
headers.extend(
self.headers
.iter()
.map(|(name, value)| Header { name: name.clone(), value: value.clone() }),
);
let response = http_client.post(self.endpoint_url.clone(), headers, body).await?;
if !(200..300).contains(&response.status) {
return Err(TokenSourceError::Server {
status: response.status,
body: String::from_utf8_lossy(&response.body).into_owned(),
});
}
let connection_details = serde_json::from_slice::<TokenSourceResponse>(&response.body)?;
Ok(connection_details)
}
}
pub fn endpoint(endpoint_url: impl Into<String>) -> TokenSourceEndpoint {
TokenSourceEndpoint { endpoint_url: endpoint_url.into(), headers: HashMap::new() }
}
impl TokenSourceEndpoint {
pub fn with_header(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.headers.insert(key.into(), value.into());
self
}
pub fn with_headers(
mut self,
value: impl IntoIterator<Item = (impl Into<String>, impl Into<String>)>,
) -> Self {
self.headers.extend(value.into_iter().map(|(k, v)| (k.into(), v.into())));
self
}
}
pub struct TokenSourceDevelopmentTokenServer {
token_source_endpoint: TokenSourceEndpoint,
}
#[async_trait]
impl TokenSourceConfigurable for TokenSourceDevelopmentTokenServer {
async fn fetch(
&self,
options: &TokenSourceFetchOptions,
) -> TokenSourceResult<TokenSourceResponse> {
self.token_source_endpoint.fetch(options).await
}
}
pub fn development_token_server(
token_server_id: impl Into<String>,
) -> TokenSourceDevelopmentTokenServer {
TokenSourceDevelopmentTokenServer {
token_source_endpoint: endpoint(DEVELOPMENT_TOKEN_SERVER_ENDPOINT_URL)
.with_header(DEVELOPMENT_TOKEN_SERVER_ID_HEADER, token_server_id),
}
}