ytmapi_rs/
builder.rs

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