deboa_extras/http/utils/file.rs
1use deboa::{
2 errors::{DeboaError, IoError},
3 response::DeboaResponse,
4 Result,
5};
6use futures::StreamExt;
7use std::{fs::File, io::Write, path::Path};
8
9/// Helper type to write a DeboaResponse to a file.
10pub struct ToFile {
11 response: DeboaResponse,
12}
13
14/// Trait to convert a DeboaResponse into a file writer.
15///
16/// This trait allows converting a DeboaResponse into a ToFile helper type
17/// which provides async file writing capabilities.
18///
19/// # Example
20/// ``` rust, compile_fail
21/// use deboa::{request::get, Client};
22/// use deboa_extras::http::utils::file::IntoFile;
23///
24/// let mut client = Client::new();
25/// let response = get("https://example.com")
26/// .send_with(&mut client)
27/// .await
28/// .unwrap();
29/// response
30/// .into_file()
31/// .save("output.txt", None)
32/// .await
33/// .unwrap();
34/// ```
35pub trait IntoFile {
36 fn into_file(self) -> ToFile;
37}
38
39impl IntoFile for DeboaResponse {
40 fn into_file(self) -> ToFile {
41 ToFile { response: self }
42 }
43}
44
45impl ToFile {
46 ///
47 /// Save the response body to a file asynchronously.
48 ///
49 /// # Arguments
50 /// * `path` - The path where the file will be saved
51 /// * `on_progress` - Optional callback function that receives the number of bytes written
52 ///
53 /// # Returns
54 /// * `Result<()>` - Ok if successful, Err with IoError if failed
55 ///
56 /// # Examples
57 /// ``` rust, compile_fail
58 ///
59 /// use deboa::{request::get, Deboa};
60 /// use deboa_extras::http::utils::file::IntoFile;
61 ///
62 /// let mut client = Deboa::default();
63 /// let response = get("https://example.com").send_with(&mut client).await?;
64 /// response.into_file().save("output.txt", None).await?;
65 /// ```
66 pub async fn save<P, EV>(self, path: P, on_progress: Option<EV>) -> Result<()>
67 where
68 P: AsRef<Path> + Send,
69 EV: Fn(u64) + Send + Sync + 'static,
70 {
71 let file = File::create(path.as_ref());
72 if let Err(err) = file {
73 return Err(DeboaError::Io(IoError::File { message: err.to_string() }));
74 }
75
76 let mut file = file.unwrap();
77 let mut stream = self
78 .response
79 .stream();
80 while let Some(frame) = stream.next().await {
81 if let Ok(chunk) = frame {
82 if let Some(data) = chunk.data_ref() {
83 if let Some(ref on_progress) = on_progress {
84 on_progress(data.len() as u64);
85 }
86 if let Err(err) = file.write(data) {
87 return Err(DeboaError::Io(IoError::File { message: err.to_string() }));
88 }
89 }
90 }
91 }
92
93 if let Err(err) = file.flush() {
94 return Err(DeboaError::Io(IoError::File { message: err.to_string() }));
95 }
96
97 Ok(())
98 }
99}