1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
//! ## Examples
//!
//! ### Create Clients
//!
//! Without authentication:
//!
//! ```rust
//! use bombay::client::Client;
//! let mc = Client::default();
//! ```
//!
//! With authentication:
//!
//! ```rust
//! use bombay::client::{auth::SignInOutcome, Error, Client};
//! use std::thread;
//! use std::time::Duration;
//! use totp_rs::{Algorithm, Secret, TOTP};
//!
//! let mut client_unauth = Client::default();
//!
//! let outcome = client_unauth
//! .sign_in(
//! env!("MC_EMAIL").to_owned(),
//! env!("MC_PASSWORD").to_owned(),
//! )
//! .expect("Failed to sign in");
//!
//! let client = match outcome {
//! // User doesn't have 2FA
//! SignInOutcome::Authenticated(new_client) => Ok(new_client),
//! // User has 2FA with email. Every 5 seconds, check if they've confirmed,
//! // up to 300 times.
//! SignInOutcome::Email(email_callback) => {
//! let mut email_authed_client = Err(Error::SignIn(
//! "Test failed, email confirmation took too long.",
//! ));
//! let mut attempts = 0;
//! while attempts < 300 {
//! attempts += 1;
//! thread::sleep(Duration::from_secs(5));
//! let client_res = email_callback(&mut client_unauth);
//! if let Ok(client) = client_res {
//! email_authed_client = Ok(client);
//! break;
//! }
//! }
//!
//! email_authed_client
//! }
//! // User has 2FA with authenticator app. Try code from String.
//! SignInOutcome::TOTP(totp_callback) => {
//! let totp_secret = Secret::Encoded(env!("MC_TOTP_SECRET").to_owned());
//! let totp_gen = TOTP::new(
//! Algorithm::SHA1,
//! 6,
//! 1,
//! 30,
//! totp_secret.to_bytes().unwrap(),
//! )
//! .unwrap();
//! totp_callback(&mut client_unauth, totp_gen.generate_current().unwrap())
//! }
//! }
//! .expect("Failed to sign in");
//! ```
//!
//! ### Fetch Some Details
//!
//! ```rust
//! use bombay::client::{Error, Client, PaginationParameters, RequestParameters};
//! use std::error;
//! use uuid::uuid;
//!
//! fn main() -> Result<(), Box<dyn error::Error>> {
//! // We don't need authentication to fetch some information.
//! let mc = Client::default();
//!
//! // Lets search for one of my favorite artists and bail if there are errors.
//! let search_results = mc
//! .artist()
//! .get_all(Some(RequestParameters::from_search("Grant".to_owned())))?;
//!
//! // I also expect some data in the response.
//! let artists = search_results
//! .data
//! .ok_or(Error::Message("Oh no! Where did Grant go?!"))?;
//!
//! // And not empty data!.
//! let grant = artists
//! .get(0)
//! .ok_or(Error::Message("Oh no! Where did Grant go?!"))?;
//!
//! // Lets make sure it's him.
//! if grant.id != uuid!("27063fd3-4fba-4119-9af0-5001e925b0d2") {
//! return Err(Box::new(Error::Message(
//! "We found someone, but not Grant! Hmmm...",
//! )));
//! }
//!
//! // Alright lets learn about Grant!
//! let about_grant = grant.about.as_ref();
//! let alt_about = r#"
//! Grant's music makes any moment better.
//! You'll find out how many emotions you can feel at once!
//! "#
//! .to_owned();
//! println!("{}:\n{}\n", &grant.name, about_grant.unwrap_or(&alt_about));
//!
//!
//! if let Some(active_years) = &grant.active_years {
//! let years: Vec<String> = active_years
//! .iter()
//! .map(|year| year.to_string())
//! .collect();
//! println!(
//! "And he's been pretty busy all these years: {}\n",
//! years.join(", ")
//! );
//! }
//!
//! // Lets get three releases from Grant.
//! let releases_result = mc.release().get_by_artist_name_uri(
//! &grant.uri,
//! Some(RequestParameters::from_pagination(PaginationParameters {
//! limit: 3,
//! offset: 0,
//! })),
//! )?;
//!
//! let releases = releases_result.data.ok_or(Error::Message(
//! "Grant lost his releases, help!",
//! ))?;
//!
//! if releases.len() != 3 {
//! return Err(Box::new(Error::Message(
//! "We needed three releases from Grant. Hmmm..."
//! )));
//! }
//!
//! println!("Listen to his work in: ");
//! for release in &releases {
//! println!(
//! " {} by {}. A {} released on {}.",
//! release.get_title(),
//! release.get_artists(),
//! release.get_type(),
//! release.get_date()
//! );
//! }
//! println!();
//!
//! if let Some(links) = &grant.links {
//! println!("Follow Grant on: ");
//! for link in links {
//! println!(" {} - {}", link.platform.to_string(), link.url.to_string());
//! }
//! println!();
//! }
//!
//! Ok(())
//! }
//! ```
/// Module containing all components for the function of the API Client itself.
/// Module containing types necessary to interact with the Monstercat
/// API, that are representative of some _thing_, like an artist or playlist.