1 //! utils function
2 use chrono::prelude::*;
3 use rand::distributions::Alphanumeric;
4 use rand::{self, Rng};
5 use webbrowser;
6 
7 use std::collections::HashMap;
8 use std::fmt::Debug;
9 use std::hash::Hash;
10 use std::io;
11 use std::string::ToString;
12 
13 use super::oauth2::{SpotifyOAuth, TokenInfo};
14 
15 /// convert datetime to unix timestampe
datetime_to_timestamp(elapsed: u32) -> i6416 pub fn datetime_to_timestamp(elapsed: u32) -> i64 {
17     let utc: DateTime<Utc> = Utc::now();
18     utc.timestamp() + i64::from(elapsed)
19 }
20 /// generate `length` random chars
generate_random_string(length: usize) -> String21 pub fn generate_random_string(length: usize) -> String {
22     rand::thread_rng()
23         .sample_iter(&Alphanumeric)
24         .take(length)
25         .collect()
26 }
27 
28 /// convert map to `query_string`, for example:
29 /// convert
30 /// `{"redirect_uri":"my_uri",
31 ///  "state":"my-state"
32 ///  "scope":"test-scope"}`
33 /// to
34 /// `redirect_uri=my_uri&state=my-state&scope=test-scope`
35 /// Since hashmap is not sorted, so the order of key-value-pairs
36 /// may differ from times
convert_map_to_string< K: Debug + Eq + Hash + ToString, V: Debug + ToString, S: ::std::hash::BuildHasher, >( map: &HashMap<K, V, S>, ) -> String37 pub fn convert_map_to_string<
38     K: Debug + Eq + Hash + ToString,
39     V: Debug + ToString,
40     S: ::std::hash::BuildHasher,
41 >(
42     map: &HashMap<K, V, S>,
43 ) -> String {
44     let mut string: String = String::new();
45     for (key, value) in map.iter() {
46         string.push_str(&key.to_string());
47         string.push_str("=");
48         string.push_str(&value.to_string());
49         string.push_str("&");
50     }
51     string
52 }
53 
54 /// convert query string to map, for example:
55 /// convert
56 /// `redirect_uri=my_uri&state=my-state&scope=test-scope`
57 /// to
58 /// `{"redirect_uri":"my_uri",
59 ///  "state":"my-state"
60 ///  "scope":"test-scope"}`
convert_str_to_map(query_str: &mut str) -> HashMap<&str, &str>61 pub fn convert_str_to_map(query_str: &mut str) -> HashMap<&str, &str> {
62     let mut map: HashMap<&str, &str> = HashMap::new();
63     let tokens: Vec<&str> = query_str
64         .split('&')
65         .filter(|token| !token.is_empty())
66         .collect();
67     for token in tokens {
68         let vec: Vec<&str> = token.split('=').collect();
69         map.insert(vec[0], vec[1]);
70     }
71     map
72 }
73 
request_token(spotify_oauth: &mut SpotifyOAuth)74 pub fn request_token(spotify_oauth: &mut SpotifyOAuth) {
75     let state = generate_random_string(16);
76     let auth_url = spotify_oauth.get_authorize_url(Some(&state), None);
77     match webbrowser::open(&auth_url) {
78         Ok(_) => println!("Opened {} in your browser", auth_url),
79         Err(why) => eprintln!("Error {:?};Please navigate here [{:?}] ", why, auth_url),
80     }
81 }
82 
process_token(spotify_oauth: &mut SpotifyOAuth, input: &mut String) -> Option<TokenInfo>83 pub fn process_token(spotify_oauth: &mut SpotifyOAuth, input: &mut String) -> Option<TokenInfo> {
84     match spotify_oauth.parse_response_code(input) {
85         Some(code) => spotify_oauth.get_access_token(&code),
86         None => None,
87     }
88 }
89 
process_token_without_cache( spotify_oauth: &mut SpotifyOAuth, input: &mut String, ) -> Option<TokenInfo>90 pub fn process_token_without_cache(
91     spotify_oauth: &mut SpotifyOAuth,
92     input: &mut String,
93 ) -> Option<TokenInfo> {
94     match spotify_oauth.parse_response_code(input) {
95         Some(code) => spotify_oauth.get_access_token_without_cache(&code),
96         None => None,
97     }
98 }
99 
100 /// get tokenInfo by Authorization
get_token(spotify_oauth: &mut SpotifyOAuth) -> Option<TokenInfo>101 pub fn get_token(spotify_oauth: &mut SpotifyOAuth) -> Option<TokenInfo> {
102     match spotify_oauth.get_cached_token() {
103         Some(token_info) => Some(token_info),
104         None => {
105             request_token(spotify_oauth);
106             println!("Enter the URL you were redirected to: ");
107             let mut input = String::new();
108             match io::stdin().read_line(&mut input) {
109                 Ok(_) => process_token(spotify_oauth, &mut input),
110                 Err(_) => None,
111             }
112         }
113     }
114 }
115 
116 /// get tokenInfo by Authorization without cache.
get_token_without_cache(spotify_oauth: &mut SpotifyOAuth) -> Option<TokenInfo>117 pub fn get_token_without_cache(spotify_oauth: &mut SpotifyOAuth) -> Option<TokenInfo> {
118     request_token(spotify_oauth);
119     println!("Enter the URL you were redirected to: ");
120     let mut input = String::new();
121     match io::stdin().read_line(&mut input) {
122         Ok(_) => process_token_without_cache(spotify_oauth, &mut input),
123         Err(_) => None,
124     }
125 }
126 
127 /// get tokenInfo by authorization and code
get_token_by_code(spotify_oauth: &mut SpotifyOAuth, code: &str) -> Option<TokenInfo>128 pub fn get_token_by_code(spotify_oauth: &mut SpotifyOAuth, code: &str) -> Option<TokenInfo> {
129     spotify_oauth.get_access_token(&code)
130 }
131 
132 #[cfg(test)]
133 mod tests {
134     use super::*;
135     #[test]
test_covert_str_to_map()136     fn test_covert_str_to_map() {
137         let mut query_url = String::from("redirect_uri=my_uri&state=my-state&scope=test-scope&");
138         let parameters = convert_str_to_map(&mut query_url);
139         match parameters.get("redirect_uri") {
140             Some(redirect_uri) => {
141                 assert_eq!(redirect_uri, &"my_uri");
142                 println!("{:?}", redirect_uri);
143             }
144             None => panic!("failed"),
145         }
146     }
147     #[test]
test_convert_map_to_string()148     fn test_convert_map_to_string() {
149         let mut map = HashMap::new();
150         map.insert("redirect_uri", "my_uri");
151         map.insert("state", "my-state");
152         map.insert("scope", "test-scope");
153         let result = convert_map_to_string(&map);
154         // hashmap is not sorted, so the order of key-value-pairs will not
155         // follow the insert order
156         assert!(result.contains("redirect_uri=my_uri&"));
157         assert!(result.contains("state=my-state&"));
158         assert!(result.contains("scope=test-scope&"));
159     }
160 }
161