Skip to content

Commit ee341ef

Browse files
authored
feat: Return paths of saved image files (#34)
* feat: return paths to saved images * doc: updated readme * doc: update examples
1 parent 00e8a3a commit ee341ef

File tree

5 files changed

+46
-27
lines changed

5 files changed

+46
-27
lines changed

async-openai/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
8181
// Download and save images to ./data directory.
8282
// Each url is downloaded and saved in dedicated Tokio task.
8383
// Directory is created if it doesn't exist.
84-
response.save("./data").await?;
84+
let paths = response.save("./data").await?;
85+
86+
paths
87+
.iter()
88+
.for_each(|path| println!("Image file path: {}", path.display()));
8589

8690
Ok(())
8791
}

async-openai/src/download.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ fn create_paths<P: AsRef<Path>>(url: &Url, base_dir: P) -> (PathBuf, PathBuf) {
2121
(dir, path)
2222
}
2323

24-
pub(crate) async fn download_url<P: AsRef<Path>>(url: &str, dir: P) -> Result<(), OpenAIError> {
24+
pub(crate) async fn download_url<P: AsRef<Path>>(
25+
url: &str,
26+
dir: P,
27+
) -> Result<PathBuf, OpenAIError> {
2528
let parsed_url = Url::parse(url).map_err(|e| OpenAIError::FileSaveError(e.to_string()))?;
2629
let response = reqwest::get(url)
2730
.await
@@ -41,7 +44,7 @@ pub(crate) async fn download_url<P: AsRef<Path>>(url: &str, dir: P) -> Result<()
4144
.map_err(|e| OpenAIError::FileSaveError(e.to_string()))?;
4245

4346
tokio::fs::write(
44-
file_path,
47+
file_path.as_path(),
4548
response
4649
.bytes()
4750
.await
@@ -50,10 +53,10 @@ pub(crate) async fn download_url<P: AsRef<Path>>(url: &str, dir: P) -> Result<()
5053
.await
5154
.map_err(|e| OpenAIError::FileSaveError(e.to_string()))?;
5255

53-
Ok(())
56+
Ok(file_path)
5457
}
5558

56-
pub(crate) async fn save_b64<P: AsRef<Path>>(b64: &str, dir: P) -> Result<(), OpenAIError> {
59+
pub(crate) async fn save_b64<P: AsRef<Path>>(b64: &str, dir: P) -> Result<PathBuf, OpenAIError> {
5760
let filename: String = rand::thread_rng()
5861
.sample_iter(&Alphanumeric)
5962
.take(10)
@@ -65,11 +68,11 @@ pub(crate) async fn save_b64<P: AsRef<Path>>(b64: &str, dir: P) -> Result<(), Op
6568
let path = PathBuf::from(dir.as_ref()).join(filename);
6669

6770
tokio::fs::write(
68-
path,
71+
path.as_path(),
6972
base64::decode(b64).map_err(|e| OpenAIError::FileSaveError(e.to_string()))?,
7073
)
7174
.await
7275
.map_err(|e| OpenAIError::FileSaveError(e.to_string()))?;
7376

74-
Ok(())
77+
Ok(path)
7578
}

async-openai/src/types/impls.rs

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ impl Display for ResponseFormat {
118118
}
119119

120120
impl ImageResponse {
121-
pub async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<(), OpenAIError> {
121+
/// Save each image in a dedicated Tokio task and return paths to saved files.
122+
/// For [ResponseFormat::Url] each file is downloaded in dedicated Tokio task.
123+
pub async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<Vec<PathBuf>, OpenAIError> {
122124
let exists = match Path::try_exists(dir.as_ref()) {
123125
Ok(exists) => exists,
124126
Err(e) => return Err(OpenAIError::FileSaveError(e.to_string())),
@@ -135,38 +137,40 @@ impl ImageResponse {
135137
handles.push(tokio::spawn(async move { id.save(dir_buf).await }));
136138
}
137139

138-
let result = futures::future::join_all(handles).await;
139-
140-
let errors: Vec<OpenAIError> = result
141-
.into_iter()
142-
.filter(|r| r.is_err() || r.as_ref().ok().unwrap().is_err())
143-
.map(|r| match r {
144-
Err(e) => OpenAIError::FileSaveError(e.to_string()),
145-
Ok(inner) => inner.err().unwrap(),
146-
})
147-
.collect();
140+
let results = futures::future::join_all(handles).await;
141+
let mut errors = vec![];
142+
let mut paths = vec![];
143+
144+
for result in results {
145+
match result {
146+
Ok(inner) => match inner {
147+
Ok(path) => paths.push(path),
148+
Err(e) => errors.push(e),
149+
},
150+
Err(e) => errors.push(OpenAIError::FileSaveError(e.to_string())),
151+
}
152+
}
148153

149-
if errors.len() > 0 {
154+
if errors.is_empty() {
155+
Ok(paths)
156+
} else {
150157
Err(OpenAIError::FileSaveError(
151158
errors
152159
.into_iter()
153160
.map(|e| e.to_string())
154161
.collect::<Vec<String>>()
155162
.join("; "),
156163
))
157-
} else {
158-
Ok(())
159164
}
160165
}
161166
}
162167

163168
impl ImageData {
164-
async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<(), OpenAIError> {
169+
async fn save<P: AsRef<Path>>(&self, dir: P) -> Result<PathBuf, OpenAIError> {
165170
match self {
166-
ImageData::Url(url) => download_url(url, dir).await?,
167-
ImageData::B64Json(b64_json) => save_b64(b64_json, dir).await?,
171+
ImageData::Url(url) => download_url(url, dir).await,
172+
ImageData::B64Json(b64_json) => save_b64(b64_json, dir).await,
168173
}
169-
Ok(())
170174
}
171175
}
172176

examples/create-image-b64-json/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
2222
// Response already contains image data in base64 format.
2323
// Save each image to ./data directory in dedicated Tokio task.
2424
// Directory is created if it doesn't exist.
25-
response.save("./data").await?;
25+
let paths = response.save("./data").await?;
26+
27+
paths
28+
.iter()
29+
.for_each(|path| println!("Image file path: {}", path.display()));
2630

2731
Ok(())
2832
}

examples/create-image/src/main.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
2222
// Download and save images to ./data directory.
2323
// Each url is downloaded and saved in dedicated Tokio task.
2424
// Directory is created if it doesn't exist.
25-
response.save("./data").await?;
25+
let paths = response.save("./data").await?;
26+
27+
paths
28+
.iter()
29+
.for_each(|path| println!("Image file path: {}", path.display()));
2630

2731
Ok(())
2832
}

0 commit comments

Comments
 (0)