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