Skip to main content

ytmapi_rs/
builder.rs

1//! Builder implementation for YtMusic, to allow more complicated construction.
2use crate::auth::AuthToken;
3use crate::auth::noauth::NoAuthToken;
4// NOTE: Example requires feature, so it is conditionally built.
5#[cfg_attr(
6    feature = "rustls",
7    doc = r##"
8## Example
9Basic usage with a pre-created cookie file forcing use of rustls
10```no_run
11#[tokio::main]
12pub async fn main() -> Result<(), ytmapi_rs::Error> {
13    let cookie_path = std::path::Path::new("./cookie.txt");
14    let yt = ytmapi_rs::builder::YtMusicBuilder::new_rustls_tls()
15        .with_browser_token_cookie_file(cookie_path)
16        .build()
17        .await?;
18    yt.get_search_suggestions("Beatles").await?;
19    let result = yt.get_search_suggestions("Beatles").await?;
20    println!("{:?}", result);
21    Ok(())
22}
23```
24"##
25)]
26use crate::{
27    Result, YtMusic,
28    auth::{BrowserToken, OAuthToken},
29    client::Client,
30};
31use std::path::Path;
32
33#[derive(Default)]
34pub enum ClientOptions {
35    #[default]
36    Default,
37    #[cfg(feature = "rustls")]
38    Rustls,
39    #[cfg(feature = "native-tls")]
40    NativeTls,
41    Existing(Client),
42}
43
44/// Helper struct for YtMusicBuilder.
45pub struct NoToken;
46/// Helper struct for YtMusicBuilder.
47pub struct FromCookie(String);
48/// Helper struct for YtMusicBuilder.
49pub struct FromCookieFile<T>(T);
50
51/// Builder to build more complex YtMusic.
52pub struct YtMusicBuilder<T> {
53    client_options: ClientOptions,
54    token: T,
55}
56
57impl<T> YtMusicBuilder<T> {
58    pub fn with_default_tls(mut self) -> Self {
59        self.client_options = ClientOptions::Default;
60        self
61    }
62    pub fn with_client(mut self, client: Client) -> Self {
63        self.client_options = ClientOptions::Existing(client);
64        self
65    }
66    #[cfg(feature = "rustls")]
67    #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
68    pub fn with_rustls_tls(mut self) -> Self {
69        self.client_options = ClientOptions::Rustls;
70        self
71    }
72    #[cfg(feature = "native-tls")]
73    #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
74    pub fn with_native_tls(mut self) -> Self {
75        self.client_options = ClientOptions::NativeTls;
76        self
77    }
78    pub fn with_auth_token<A: AuthToken>(self, token: A) -> YtMusicBuilder<A> {
79        let YtMusicBuilder {
80            client_options,
81            token: _,
82        } = self;
83        YtMusicBuilder {
84            client_options,
85            token,
86        }
87    }
88    // TODO: Improve how this handles building client.
89    pub fn with_browser_token_cookie(self, cookie: String) -> YtMusicBuilder<FromCookie> {
90        let YtMusicBuilder {
91            client_options,
92            token: _,
93        } = self;
94        let token = FromCookie(cookie);
95        YtMusicBuilder {
96            client_options,
97            token,
98        }
99    }
100    // TODO: Improve how this handles building client.
101    pub fn with_browser_token_cookie_file<P: AsRef<Path>>(
102        self,
103        cookie_file: P,
104    ) -> YtMusicBuilder<FromCookieFile<P>> {
105        let YtMusicBuilder {
106            client_options,
107            token: _,
108        } = self;
109        let token = FromCookieFile(cookie_file);
110        YtMusicBuilder {
111            client_options,
112            token,
113        }
114    }
115    #[deprecated = "Use generic `with_auth_token` instead"]
116    pub fn with_browser_token(self, token: BrowserToken) -> YtMusicBuilder<BrowserToken> {
117        let YtMusicBuilder {
118            client_options,
119            token: _,
120        } = self;
121        YtMusicBuilder {
122            client_options,
123            token,
124        }
125    }
126    #[deprecated = "Use generic `with_auth_token` instead"]
127    pub fn with_oauth_token(self, token: OAuthToken) -> YtMusicBuilder<OAuthToken> {
128        let YtMusicBuilder {
129            client_options,
130            token: _,
131        } = self;
132        YtMusicBuilder {
133            client_options,
134            token,
135        }
136    }
137}
138impl YtMusicBuilder<FromCookie> {
139    pub async fn build(self) -> Result<YtMusic<BrowserToken>> {
140        let YtMusicBuilder {
141            client_options,
142            token: FromCookie(cookie),
143        } = self;
144        let client = build_client(client_options)?;
145        let token = BrowserToken::from_str(cookie.as_ref(), &client).await?;
146        Ok(YtMusic { client, token })
147    }
148}
149impl<P: AsRef<Path>> YtMusicBuilder<FromCookieFile<P>> {
150    pub async fn build(self) -> Result<YtMusic<BrowserToken>> {
151        let YtMusicBuilder {
152            client_options,
153            token: FromCookieFile(cookie_file),
154        } = self;
155        let client = build_client(client_options)?;
156        let token = BrowserToken::from_cookie_file(cookie_file, &client).await?;
157        Ok(YtMusic { client, token })
158    }
159}
160impl YtMusicBuilder<NoToken> {
161    // This lint is a little confusing in this case, as we do not want different
162    // default implementations for YtMusicBuilder<T> depending on T. There
163    // should only be one way to construct a YtMusicBuilder with T = NoToken.
164    #[allow(clippy::new_without_default)]
165    pub fn new() -> YtMusicBuilder<NoToken> {
166        YtMusicBuilder {
167            client_options: ClientOptions::Default,
168            token: NoToken,
169        }
170    }
171    pub fn new_with_client(client: Client) -> YtMusicBuilder<NoToken> {
172        YtMusicBuilder {
173            client_options: ClientOptions::Existing(client),
174            token: NoToken,
175        }
176    }
177    #[cfg(feature = "rustls")]
178    #[cfg_attr(docsrs, doc(cfg(feature = "rustls")))]
179    pub fn new_rustls_tls() -> YtMusicBuilder<NoToken> {
180        YtMusicBuilder {
181            client_options: ClientOptions::Rustls,
182            token: NoToken,
183        }
184    }
185    #[cfg(feature = "native-tls")]
186    #[cfg_attr(docsrs, doc(cfg(feature = "native-tls")))]
187    pub fn new_native_tls() -> Self {
188        YtMusicBuilder {
189            client_options: ClientOptions::NativeTls,
190            token: NoToken,
191        }
192    }
193    pub async fn build(self) -> Result<YtMusic<NoAuthToken>> {
194        let YtMusicBuilder { client_options, .. } = self;
195        let client = build_client(client_options)?;
196        let token = NoAuthToken::new(&client).await?;
197        Ok(YtMusic { client, token })
198    }
199}
200
201impl<A: AuthToken> YtMusicBuilder<A> {
202    pub fn build(self) -> Result<YtMusic<A>> {
203        let YtMusicBuilder {
204            client_options,
205            token,
206        } = self;
207        let client = build_client(client_options)?;
208        Ok(YtMusic { client, token })
209    }
210}
211
212fn build_client(client_options: ClientOptions) -> Result<Client> {
213    match client_options {
214        ClientOptions::Default => Client::new(),
215        #[cfg(feature = "rustls")]
216        ClientOptions::Rustls => Client::new_rustls_tls(),
217        #[cfg(feature = "native-tls")]
218        ClientOptions::NativeTls => Client::new_native_tls(),
219        ClientOptions::Existing(client) => Ok(client),
220    }
221}