use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use serde::de::DeserializeOwned;
use crate::builder::types::{ConfQueryInner, Resp, RoomQueryInner};
use crate::config::StreamConfig;
use crate::errors::{BoxedError, BuildError};
#[cfg(test)]
mod tests;
mod types;
#[cfg(feature = "not-send")]
pub trait Requester {
fn get_json<T: DeserializeOwned>(
&self,
url: &str,
) -> Pin<Box<dyn Future<Output = Result<T, BoxedError>> + '_>>;
}
#[cfg(not(feature = "not-send"))]
pub trait Requester: Send + Sync {
fn get_json<T: DeserializeOwned>(
&self,
url: &str,
) -> Pin<Box<dyn Future<Output = Result<T, BoxedError>> + Send + '_>>;
}
#[doc(hidden)]
pub enum BF {}
#[doc(hidden)]
pub enum BN {}
#[derive(Debug)]
pub struct ConfigBuilder<H, R, U, T, S> {
http: H,
room_id: Option<u64>,
uid: Option<u64>,
token: Option<String>,
servers: Option<Vec<String>>,
__marker: PhantomData<(R, U, T, S)>,
}
impl<H: Default> ConfigBuilder<H, BN, BN, BN, BN> {
#[allow(clippy::new_without_default)]
#[must_use]
pub fn new() -> Self {
Self::new_with_client(H::default())
}
}
impl<H> ConfigBuilder<H, BN, BN, BN, BN> {
#[must_use]
pub const fn new_with_client(client: H) -> Self {
Self {
http: client,
room_id: None,
uid: None,
token: None,
servers: None,
__marker: PhantomData,
}
}
}
impl<H, R, U, T, S> ConfigBuilder<H, R, U, T, S> {
#[allow(clippy::missing_const_for_fn)] fn cast<R2, U2, T2, S2>(self) -> ConfigBuilder<H, R2, U2, T2, S2> {
ConfigBuilder {
http: self.http,
room_id: self.room_id,
uid: self.uid,
token: self.token,
servers: self.servers,
__marker: PhantomData,
}
}
}
impl<H, R, U, T, S> ConfigBuilder<H, R, U, T, S> {
#[must_use]
pub fn room_id(mut self, room_id: u64) -> ConfigBuilder<H, BF, U, T, S> {
self.room_id = Some(room_id);
self.cast()
}
#[must_use]
pub fn uid(mut self, uid: u64) -> ConfigBuilder<H, R, BF, T, S> {
self.uid = Some(uid);
self.cast()
}
#[must_use]
pub fn token(mut self, token: &str) -> ConfigBuilder<H, R, U, BF, S> {
self.token = Some(token.to_string());
self.cast()
}
#[must_use]
pub fn servers(mut self, servers: &[String]) -> ConfigBuilder<H, R, U, T, BF> {
self.servers = Some(servers.to_vec());
self.cast()
}
}
impl<H, R, U, T, S> ConfigBuilder<H, R, U, T, S>
where
H: Requester,
R: Send + Sync,
U: Send + Sync,
T: Send + Sync,
S: Send + Sync,
{
pub async fn by_uid(mut self, uid: u64) -> Result<ConfigBuilder<H, BF, BF, T, S>, BuildError> {
let resp: Resp<RoomQueryInner> = self
.http
.get_json(&*format!(
"https://api.live.bilibili.com/bili/living_v2/{}",
uid
))
.await
.map_err(BuildError)?;
let room_id = resp.room_id();
self.room_id = Some(room_id);
self.uid = Some(uid);
Ok(self.cast())
}
pub async fn fetch_conf(mut self) -> Result<ConfigBuilder<H, R, U, BF, BF>, BuildError> {
let resp: Resp<ConfQueryInner> = self
.http
.get_json("https://api.live.bilibili.com/room/v1/Danmu/getConf")
.await
.map_err(BuildError)?;
self.token = Some(resp.token().to_string());
self.servers = Some(resp.servers());
Ok(self.cast())
}
}
impl<H> ConfigBuilder<H, BF, BF, BF, BF> {
#[allow(clippy::missing_panics_doc)]
pub fn build(self) -> StreamConfig {
StreamConfig::new(
self.room_id.unwrap(),
self.uid.unwrap(),
self.token.unwrap(),
self.servers.unwrap(),
)
}
}