Skip to main content

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}