1use std::path::Path;
2
3use reqwest::Body;
4use tokio::fs::File;
5use tokio_util::codec::{BytesCodec, FramedRead};
6
7use crate::error::OpenAIError;
8use crate::types::InputSource;
9
10pub(crate) async fn file_stream_body(source: InputSource) -> Result<Body, OpenAIError> {
11 let body = match source {
12 InputSource::Path { path } => {
13 let file = File::open(path)
14 .await
15 .map_err(|e| OpenAIError::FileReadError(e.to_string()))?;
16 let stream = FramedRead::new(file, BytesCodec::new());
17 Body::wrap_stream(stream)
18 }
19 _ => {
20 return Err(OpenAIError::FileReadError(
21 "Cannot create stream from non-file source".to_string(),
22 ))
23 }
24 };
25 Ok(body)
26}
27
28pub(crate) async fn create_file_part(
30 source: InputSource,
31) -> Result<reqwest::multipart::Part, OpenAIError> {
32 let (stream, file_name) = match source {
33 InputSource::Path { path } => {
34 let file_name = path
35 .file_name()
36 .ok_or_else(|| {
37 OpenAIError::FileReadError(format!(
38 "cannot extract file name from {}",
39 path.display()
40 ))
41 })?
42 .to_str()
43 .unwrap()
44 .to_string();
45
46 (
47 file_stream_body(InputSource::Path { path }).await?,
48 file_name,
49 )
50 }
51 InputSource::Bytes { filename, bytes } => (Body::from(bytes), filename),
52 InputSource::VecU8 { filename, vec } => (Body::from(vec), filename),
53 };
54
55 let file_part = reqwest::multipart::Part::stream(stream)
56 .file_name(file_name)
57 .mime_str("application/octet-stream")
58 .unwrap();
59
60 Ok(file_part)
61}
62
63pub(crate) fn create_all_dir<P: AsRef<Path>>(dir: P) -> Result<(), OpenAIError> {
64 let exists = match Path::try_exists(dir.as_ref()) {
65 Ok(exists) => exists,
66 Err(e) => return Err(OpenAIError::FileSaveError(e.to_string())),
67 };
68
69 if !exists {
70 std::fs::create_dir_all(dir.as_ref())
71 .map_err(|e| OpenAIError::FileSaveError(e.to_string()))?;
72 }
73
74 Ok(())
75}