1
- use std:: fmt;
1
+ use std:: process:: { Command as ProcessCommand , Stdio } ;
2
+ use std:: { fmt, fs} ;
2
3
3
- use reqwest:: blocking:: multipart;
4
+ use reqwest:: blocking:: multipart:: Form ;
5
+ use reqwest:: blocking:: Client ;
6
+ use reqwest:: Error ;
4
7
use serde:: { Deserialize , Serialize } ;
5
8
use serde_with:: { serde_as, DefaultOnNull } ;
6
9
use tabled:: Tabled ;
7
10
11
+ use crate :: put;
12
+
8
13
#[ derive( Debug , Serialize , Deserialize ) ]
9
14
pub struct FileSize ( u64 ) ;
10
15
@@ -33,18 +38,15 @@ pub struct FilesResponse {
33
38
}
34
39
35
40
/// Returns the user's files.
36
- pub fn list (
37
- api_token : String ,
38
- parent_id : u32 ,
39
- ) -> Result < FilesResponse , Box < dyn std:: error:: Error > > {
40
- let client = reqwest:: blocking:: Client :: new ( ) ;
41
+ pub fn list ( client : & Client , api_token : & String , parent_id : u32 ) -> Result < FilesResponse , Error > {
41
42
let response: FilesResponse = client
42
43
. get ( format ! (
43
44
"https://api.put.io/v2/files/list?parent_id={parent_id}"
44
45
) )
45
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
46
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
46
47
. send ( ) ?
47
48
. json ( ) ?;
49
+
48
50
Ok ( response)
49
51
}
50
52
@@ -56,26 +58,27 @@ pub struct SearchResponse {
56
58
57
59
/// Searches files for given keyword.
58
60
pub fn search (
59
- api_token : String ,
60
- query : String ,
61
- ) -> Result < SearchResponse , Box < dyn std :: error :: Error > > {
62
- let client = reqwest :: blocking :: Client :: new ( ) ;
61
+ client : & Client ,
62
+ api_token : & String ,
63
+ query : & String ,
64
+ ) -> Result < SearchResponse , Error > {
63
65
let response: SearchResponse = client
64
66
. get ( format ! ( "https://api.put.io/v2/files/search?query={query}" ) )
65
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
67
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
66
68
. send ( ) ?
67
69
. json ( ) ?;
70
+
68
71
Ok ( response)
69
72
}
70
73
71
74
/// Delete file(s)
72
- pub fn delete ( api_token : String , file_id : String ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
73
- let client = reqwest :: blocking :: Client :: new ( ) ;
74
- let form = multipart :: Form :: new ( ) . text ( "file_ids" , file_id ) ;
75
+ pub fn delete ( client : & Client , api_token : & String , file_id : & str ) -> Result < ( ) , Error > {
76
+ let form : Form = Form :: new ( ) . text ( "file_ids" , file_id . to_owned ( ) ) ;
77
+
75
78
client
76
79
. post ( "https://api.put.io/v2/files/delete" )
77
80
. multipart ( form)
78
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
81
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
79
82
. send ( ) ?;
80
83
81
84
Ok ( ( ) )
@@ -87,70 +90,72 @@ pub struct UrlResponse {
87
90
}
88
91
89
92
/// Returns a download URL for a given file.
90
- pub fn url ( api_token : String , file_id : u32 ) -> Result < UrlResponse , Box < dyn std:: error:: Error > > {
91
- let client = reqwest:: blocking:: Client :: new ( ) ;
93
+ pub fn url ( client : & Client , api_token : & String , file_id : u32 ) -> Result < UrlResponse , Error > {
92
94
let response: UrlResponse = client
93
95
. get ( format ! ( "https://api.put.io/v2/files/{file_id}/url" ) )
94
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
96
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
95
97
. send ( ) ?
96
98
. json ( ) ?;
99
+
97
100
Ok ( response)
98
101
}
99
102
100
103
/// Moves a file to a different parent
101
104
pub fn mv (
102
- api_token : String ,
103
- file_id : String ,
105
+ client : & Client ,
106
+ api_token : & String ,
107
+ file_id : u32 ,
104
108
new_parent_id : u32 ,
105
- ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
106
- let client = reqwest:: blocking:: Client :: new ( ) ;
107
- let form = multipart:: Form :: new ( )
108
- . text ( "file_ids" , file_id)
109
+ ) -> Result < ( ) , Error > {
110
+ let form: Form = Form :: new ( )
111
+ . text ( "file_ids" , file_id. to_string ( ) )
109
112
. text ( "parent_id" , new_parent_id. to_string ( ) ) ;
113
+
110
114
client
111
115
. post ( "https://api.put.io/v2/files/move" )
112
116
. multipart ( form)
113
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
117
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
114
118
. send ( ) ?;
115
119
116
120
Ok ( ( ) )
117
121
}
118
122
119
123
/// Renames a file
120
124
pub fn rename (
121
- api_token : String ,
125
+ client : & Client ,
126
+ api_token : & String ,
122
127
file_id : u32 ,
123
- new_name : String ,
124
- ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
125
- let client = reqwest:: blocking:: Client :: new ( ) ;
126
- let form = multipart:: Form :: new ( )
128
+ new_name : & String ,
129
+ ) -> Result < ( ) , Error > {
130
+ let form = Form :: new ( )
127
131
. text ( "file_id" , file_id. to_string ( ) )
128
- . text ( "name" , new_name) ;
132
+ . text ( "name" , new_name. to_owned ( ) ) ;
133
+
129
134
client
130
135
. post ( "https://api.put.io/v2/files/rename" )
131
136
. multipart ( form)
132
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
137
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
133
138
. send ( ) ?;
134
139
135
140
Ok ( ( ) )
136
141
}
137
142
138
143
/// Extracts ZIP and RAR archives
139
- pub fn extract ( api_token : String , file_id : String ) -> Result < ( ) , Box < dyn std :: error :: Error > > {
140
- let client = reqwest :: blocking :: Client :: new ( ) ;
141
- let form = multipart :: Form :: new ( ) . text ( "file_ids" , file_id ) ;
144
+ pub fn extract ( client : & Client , api_token : & String , file_id : u32 ) -> Result < ( ) , Error > {
145
+ let form : Form = Form :: new ( ) . text ( "file_ids" , file_id . to_string ( ) ) ;
146
+
142
147
client
143
148
. post ( "https://api.put.io/v2/files/extract" )
144
149
. multipart ( form)
145
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
150
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
146
151
. send ( ) ?;
147
152
148
153
Ok ( ( ) )
149
154
}
150
155
151
156
#[ derive( Debug , Serialize , Deserialize , Tabled ) ]
152
157
pub struct Extraction {
153
- pub id : u32 ,
158
+ pub id : String ,
154
159
pub name : String ,
155
160
pub status : String ,
156
161
pub message : String ,
@@ -162,14 +167,108 @@ pub struct ExtractionResponse {
162
167
}
163
168
164
169
/// Returns active extractions
165
- pub fn get_extractions (
166
- api_token : String ,
167
- ) -> Result < ExtractionResponse , Box < dyn std:: error:: Error > > {
168
- let client = reqwest:: blocking:: Client :: new ( ) ;
170
+ pub fn get_extractions ( client : & Client , api_token : & String ) -> Result < ExtractionResponse , Error > {
169
171
let response: ExtractionResponse = client
170
172
. get ( "https://api.put.io/v2/files/extract" )
171
- . header ( "authorization" , format ! ( "Bearer {}" , api_token ) )
173
+ . header ( "authorization" , format ! ( "Bearer {api_token}" ) )
172
174
. send ( ) ?
173
175
. json ( ) ?;
176
+
174
177
Ok ( response)
175
178
}
179
+
180
+ // Downloads a file or folder
181
+ pub fn download (
182
+ client : & Client ,
183
+ api_token : & String ,
184
+ file_id : u32 ,
185
+ recursive : bool ,
186
+ path : Option < & String > ,
187
+ ) -> Result < ( ) , Error > {
188
+ let files: FilesResponse =
189
+ put:: files:: list ( client, api_token, file_id) . expect ( "querying files" ) ;
190
+
191
+ match files. parent . file_type . as_str ( ) {
192
+ "FOLDER" => {
193
+ // ID is for a folder
194
+ match recursive {
195
+ true => {
196
+ // Recursively download the folder
197
+ let directory_path: String = match path {
198
+ Some ( p) => format ! ( "{}/{}" , p, files. parent. name) , // Use the provided path if there is one
199
+ None => format ! ( "./{}" , files. parent. name) ,
200
+ } ;
201
+
202
+ fs:: create_dir_all ( directory_path. clone ( ) ) . expect ( "creating directory" ) ;
203
+
204
+ for file in files. files {
205
+ download ( client, api_token, file. id , true , Some ( & directory_path) )
206
+ . expect ( "downloading file recursively" ) ;
207
+ }
208
+ }
209
+ false => {
210
+ // Create a ZIP
211
+ println ! ( "Creating ZIP..." ) ;
212
+
213
+ let zip_url: String = put:: zips:: create ( client, api_token, files. parent . id )
214
+ . expect ( "creating zip job" ) ;
215
+
216
+ println ! ( "ZIP created!" ) ;
217
+
218
+ let output_path: String = match path {
219
+ Some ( p) => format ! ( "{}/{}.zip" , p, files. parent. name) ,
220
+ None => format ! ( "./{}.zip" , files. parent. name) ,
221
+ } ;
222
+
223
+ println ! ( "Downloading: {}" , files. parent. name) ;
224
+ println ! ( "Saving to: {}\n " , output_path) ;
225
+
226
+ // https://rust-lang-nursery.github.io/rust-cookbook/os/external.html#redirect-both-stdout-and-stderr-of-child-process-to-the-same-file
227
+ ProcessCommand :: new ( "curl" )
228
+ . arg ( "-C" )
229
+ . arg ( "-" )
230
+ . arg ( "-o" )
231
+ . arg ( output_path)
232
+ . arg ( zip_url)
233
+ . stdout ( Stdio :: piped ( ) )
234
+ . spawn ( )
235
+ . expect ( "failed to run CURL command" )
236
+ . wait_with_output ( )
237
+ . expect ( "failed to run CURL command" ) ;
238
+
239
+ println ! ( "\n Download finished!\n " )
240
+ }
241
+ }
242
+ }
243
+ _ => {
244
+ // ID is for a file
245
+ let url_response: UrlResponse =
246
+ put:: files:: url ( client, api_token, file_id) . expect ( "creating download URL" ) ;
247
+
248
+ let output_path: String = match path {
249
+ Some ( p) => format ! ( "{}/{}" , p, files. parent. name) ,
250
+ None => format ! ( "./{}" , files. parent. name) ,
251
+ } ;
252
+
253
+ println ! ( "Downloading: {}" , files. parent. name) ;
254
+ println ! ( "Saving to: {}\n " , output_path) ;
255
+
256
+ // https://rust-lang-nursery.github.io/rust-cookbook/os/external.html#redirect-both-stdout-and-stderr-of-child-process-to-the-same-file
257
+ ProcessCommand :: new ( "curl" )
258
+ . arg ( "-C" )
259
+ . arg ( "-" )
260
+ . arg ( "-o" )
261
+ . arg ( output_path)
262
+ . arg ( url_response. url )
263
+ . stdout ( Stdio :: piped ( ) )
264
+ . spawn ( )
265
+ . expect ( "error while spawning curl" )
266
+ . wait_with_output ( )
267
+ . expect ( "running CURL command" ) ;
268
+
269
+ println ! ( "\n Download finished!\n " )
270
+ }
271
+ }
272
+
273
+ Ok ( ( ) )
274
+ }
0 commit comments