1mod reader;
2mod util;
3
4pub use self::reader::FileDownloadReader;
5pub use self::util::ArcError;
6use crate::Command;
7use crate::Error;
8use crate::FetchNodesResponse;
9use crate::FileKey;
10use crate::GetAttributesResponse;
11use crate::ResponseData;
12use std::future::Future;
13use std::pin::Pin;
14use tokio::io::AsyncRead;
17use tokio_stream::StreamExt;
18use tokio_util::io::StreamReader;
19
20#[derive(Debug, Clone)]
22pub struct Client {
23 pub client: crate::Client,
25 }
28
29impl Client {
30 pub fn new() -> Self {
32 Self {
33 client: crate::Client::new(),
34 }
41 }
42
43 pub fn get_attributes(
103 &self,
104 builder: GetAttributesBuilder,
105 ) -> impl Future<Output = Result<GetAttributesResponse, Error>> {
106 let command = Command::GetAttributes {
107 public_node_id: builder.public_node_id,
108 node_id: builder.node_id,
109 include_download_url: if builder.include_download_url {
110 Some(1)
111 } else {
112 None
113 },
114 };
115
116 async move {
117 let commands = [command];
118
119 let response = self
120 .client
121 .execute_commands(&commands, builder.reference_node_id.as_deref());
122
123 let response = match response.await?.swap_remove(0).into_result()? {
124 ResponseData::GetAttributes(response) => response,
125 _ => {
126 return Err(Error::UnexpectedResponseDataType);
127 }
128 };
129
130 Ok(response)
131 }
132 }
133
134 pub async fn fetch_nodes(
138 &self,
139 node_id: Option<&str>,
140 recursive: bool,
141 ) -> Result<FetchNodesResponse, Error> {
142 let command = Command::FetchNodes {
143 c: 1,
144 recursive: u8::from(recursive),
145 };
146 let mut response = self
147 .client
148 .execute_commands(std::slice::from_ref(&command), node_id)
149 .await?;
150
151 let response = response.pop().unwrap();
153 let response = response.into_result().map_err(Error::from)?;
154 let response = match response {
155 ResponseData::FetchNodes(response) => response,
156 _ => {
157 return Err(Error::UnexpectedResponseDataType);
158 }
159 };
160
161 Ok(response)
162 }
163
164 pub async fn download_file_no_verify(
169 &self,
170 file_key: &FileKey,
171 url: &str,
172 ) -> Result<FileDownloadReader<Pin<Box<dyn AsyncRead + Send + Sync>>>, Error> {
173 let response = self
174 .client
175 .client
176 .get(url)
177 .send()
178 .await?
179 .error_for_status()?;
180
181 let stream_reader = StreamReader::new(
182 response
183 .bytes_stream()
184 .map(|result| result.map_err(std::io::Error::other)),
185 );
186 let stream_reader =
187 Box::into_pin(Box::new(stream_reader) as Box<dyn AsyncRead + Send + Sync>);
188
189 let reader = FileDownloadReader::new(stream_reader, file_key, false);
190
191 Ok(reader)
192 }
193
194 pub async fn download_file(
199 &self,
200 file_key: &FileKey,
201 url: &str,
202 ) -> Result<FileDownloadReader<Pin<Box<dyn AsyncRead + Send + Sync>>>, Error> {
203 let response = self
204 .client
205 .client
206 .get(url)
207 .send()
208 .await?
209 .error_for_status()?;
210
211 let stream_reader = StreamReader::new(
212 response
213 .bytes_stream()
214 .map(|result| result.map_err(std::io::Error::other)),
215 );
216 let stream_reader =
217 Box::into_pin(Box::new(stream_reader) as Box<dyn AsyncRead + Send + Sync>);
218
219 let reader = FileDownloadReader::new(stream_reader, file_key, true);
220
221 Ok(reader)
222 }
223}
224
225impl Default for Client {
226 fn default() -> Self {
227 Self::new()
228 }
229}
230
231#[derive(Debug)]
242pub struct GetAttributesBuilder {
243 pub public_node_id: Option<String>,
247 pub node_id: Option<String>,
251 pub include_download_url: bool,
253
254 pub reference_node_id: Option<String>,
256}
257
258impl GetAttributesBuilder {
259 pub fn new() -> Self {
261 Self {
262 public_node_id: None,
263 node_id: None,
264 include_download_url: false,
265 reference_node_id: None,
266 }
267 }
268
269 pub fn public_node_id(&mut self, value: impl Into<String>) -> &mut Self {
273 self.public_node_id = Some(value.into());
274 self
275 }
276
277 pub fn node_id(&mut self, value: impl Into<String>) -> &mut Self {
281 self.node_id = Some(value.into());
282 self
283 }
284
285 pub fn include_download_url(&mut self, value: bool) -> &mut Self {
287 self.include_download_url = value;
288 self
289 }
290
291 pub fn reference_node_id(&mut self, value: impl Into<String>) -> &mut Self {
293 self.reference_node_id = Some(value.into());
294 self
295 }
296}
297
298impl Default for GetAttributesBuilder {
299 fn default() -> Self {
300 Self::new()
301 }
302}
303
304#[cfg(test)]
305mod test {
306 use super::*;
307 use crate::FolderKey;
308 use crate::test::*;
309 use tokio::io::AsyncReadExt;
310
311 #[tokio::test]
338 async fn fetch_nodes() {
339 let folder_key = FolderKey(TEST_FOLDER_KEY_DECODED);
340
341 let client = Client::new();
342 let response = client
343 .fetch_nodes(Some(TEST_FOLDER_ID), true)
344 .await
345 .expect("failed to fetch nodes");
346 assert!(response.nodes.len() == 3);
347 let file_attributes = response
348 .nodes
349 .iter()
350 .find(|file| file.id == "oLkVhYqA")
351 .expect("failed to locate file")
352 .decode_attributes(&folder_key)
353 .expect("failed to decode attributes");
354 assert!(file_attributes.name == "test");
355
356 let file_attributes = response
357 .nodes
358 .iter()
359 .find(|file| file.id == "kalwUahb")
360 .expect("failed to locate file")
361 .decode_attributes(&folder_key)
362 .expect("failed to decode attributes");
363 assert!(file_attributes.name == "test.txt");
364
365 let file_attributes = &response
366 .nodes
367 .iter()
368 .find(|file| file.id == "IGlBlD6K")
369 .expect("failed to locate file")
370 .decode_attributes(&folder_key)
371 .expect("failed to decode attributes");
372 assert!(file_attributes.name == "testfolder");
373 }
374
375 #[tokio::test]
376 async fn download_file_no_verify() {
377 let file_key = FileKey {
378 key: TEST_FILE_KEY_KEY_DECODED,
379 iv: TEST_FILE_KEY_IV_DECODED,
380 meta_mac: TEST_FILE_META_MAC_DECODED,
381 };
382
383 let client = Client::new();
384 let mut builder = GetAttributesBuilder::new();
385 builder
386 .include_download_url(true)
387 .public_node_id(TEST_FILE_ID);
388 let attributes = client
389 .get_attributes(builder)
390 .await
391 .expect("failed to get attributes");
392 let url = attributes.download_url.expect("missing download url");
393 let mut reader = client
394 .download_file_no_verify(&file_key, url.as_str())
395 .await
396 .expect("failed to get download stream");
397 let mut file = Vec::with_capacity(1024 * 1024);
398 tokio::io::copy(&mut reader, &mut file)
399 .await
400 .expect("failed to copy");
401
402 assert!(file == TEST_FILE_BYTES);
403 }
404
405 #[tokio::test]
406 async fn download_file_verify() {
407 let file_key = FileKey {
408 key: TEST_FILE_KEY_KEY_DECODED,
409 iv: TEST_FILE_KEY_IV_DECODED,
410 meta_mac: TEST_FILE_META_MAC_DECODED,
411 };
412
413 let client = Client::new();
414 {
415 let mut builder = GetAttributesBuilder::new();
416 builder
417 .include_download_url(true)
418 .public_node_id(TEST_FILE_ID);
419 let attributes = client
420 .get_attributes(builder)
421 .await
422 .expect("failed to get attributes");
423 let url = attributes.download_url.expect("missing download url");
424 let mut reader = client
425 .download_file(&file_key, url.as_str())
426 .await
427 .expect("failed to get download stream");
428 let mut file = Vec::with_capacity(1024 * 1024);
429 tokio::io::copy(&mut reader, &mut file)
430 .await
431 .expect("failed to copy");
432
433 assert!(file == TEST_FILE_BYTES);
434 }
435
436 {
437 let mut builder = GetAttributesBuilder::new();
438 builder
439 .include_download_url(true)
440 .public_node_id(TEST_FILE_ID);
441 let attributes = client
442 .get_attributes(builder)
443 .await
444 .expect("failed to get attributes");
445 let url = attributes.download_url.expect("missing download url");
446 let mut reader = client
447 .download_file(&file_key, url.as_str())
448 .await
449 .expect("failed to get download stream");
450 let mut file = Vec::new();
451 reader.read_to_end(&mut file).await.unwrap();
452
453 assert!(file == TEST_FILE_BYTES);
454 }
455 }
456}