async_openai/types/images/
sdk.rs

1use crate::{
2    download::{download_url, save_b64},
3    error::OpenAIError,
4    types::images::{Image, ImagesResponse},
5    util::create_all_dir,
6};
7use std::path::{Path, PathBuf};
8
9impl ImagesResponse {
10    /// Save each image in a dedicated Tokio task and return paths to saved files.
11    /// For [ResponseFormat::Url] each file is downloaded in dedicated Tokio task.
12    pub async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<Vec<PathBuf>, OpenAIError> {
13        create_all_dir(dir.as_ref())?;
14
15        let mut handles = vec![];
16        for id in self.data.clone() {
17            let dir_buf = PathBuf::from(dir.as_ref());
18            handles.push(tokio::spawn(async move { id.save(dir_buf).await }));
19        }
20
21        let results = futures::future::join_all(handles).await;
22        let mut errors = vec![];
23        let mut paths = vec![];
24
25        for result in results {
26            match result {
27                Ok(inner) => match inner {
28                    Ok(path) => paths.push(path),
29                    Err(e) => errors.push(e),
30                },
31                Err(e) => errors.push(OpenAIError::FileSaveError(e.to_string())),
32            }
33        }
34
35        if errors.is_empty() {
36            Ok(paths)
37        } else {
38            Err(OpenAIError::FileSaveError(
39                errors
40                    .into_iter()
41                    .map(|e| e.to_string())
42                    .collect::<Vec<String>>()
43                    .join("; "),
44            ))
45        }
46    }
47}
48
49impl Image {
50    async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, OpenAIError> {
51        match self {
52            Image::Url { url, .. } => download_url(url, dir).await,
53            Image::B64Json { b64_json, .. } => save_b64(b64_json, dir).await,
54        }
55    }
56}