use reqwest::multipart::Form;
use std::fmt::Display;
use subtp::srt::SubRip;
use subtp::vtt::WebVtt;
use crate::audio::AudioApiError;
use crate::audio::AudioApiResult;
use crate::audio::AudioModel;
use crate::audio::File;
use crate::audio::JsonResponse;
use crate::audio::JsonResponseFormatter;
use crate::audio::PlainTextResponseFormatter;
use crate::audio::SrtResponseFormatter;
use crate::audio::TextResponseFormat;
use crate::audio::TextResponseFormatter;
use crate::audio::VerboseJsonResponse;
use crate::audio::VerboseJsonResponseFormatter;
use crate::audio::VttResponseFormatter;
use crate::ApiError;
use crate::Client;
use crate::ClientError;
use crate::Prompt;
use crate::Temperature;
#[derive(Debug, Default)]
pub struct TranslationsRequestBody {
pub file: File,
pub model: AudioModel,
pub prompt: Option<Prompt>,
pub temperature: Option<Temperature>,
}
impl Display for TranslationsRequestBody {
fn fmt(
&self,
f: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
write!(f, "file: {}", self.file)?;
write!(f, ", model: {}", self.model)?;
if let Some(prompt) = self.prompt.clone() {
write!(f, ", prompt: {}", prompt)?;
}
if let Some(temperature) = self.temperature {
write!(f, ", temperature: {}", temperature)?;
}
Ok(())
}
}
impl TranslationsRequestBody {
pub fn new(
file: File,
model: AudioModel,
prompt: Option<Prompt>,
temperature: Option<Temperature>,
) -> Self {
Self {
file,
model,
prompt,
temperature,
}
}
async fn build_form<F, T>(self) -> Form
where
F: TextResponseFormat,
T: TextResponseFormatter<F>,
{
let mut form = Form::new()
.part("file", self.file.part)
.text("model", self.model.to_string())
.text("response_format", F::format());
if let Some(prompt) = self.prompt {
form = form.text("prompt", prompt.format());
}
if let Some(temperature) = self.temperature {
form = form.text("temperature", temperature.format());
}
form
}
}
async fn translate<F, T>(
client: &Client,
request_body: TranslationsRequestBody,
) -> AudioApiResult<F>
where
F: TextResponseFormat,
T: TextResponseFormatter<F>,
{
let form = request_body
.build_form::<F, T>()
.await;
let response = client
.post("https://api.openai.com/v1/audio/translations")
.multipart(form)
.send()
.await
.map_err(ClientError::HttpRequestError)?;
let status_code = response.status();
let response_text = response
.text()
.await
.map_err(ClientError::ReadResponseTextFailed)?;
if status_code.is_success() {
T::format(response_text).map_err(AudioApiError::FormatResponseFailed)
}
else {
let error_response =
serde_json::from_str(&response_text).map_err(|error| {
ClientError::ErrorResponseDeserializationFailed {
error,
text: response_text,
}
})?;
Err(ApiError {
status_code,
error_response,
}
.into())
}
}
pub(crate) async fn translate_into_json(
client: &Client,
request_body: TranslationsRequestBody,
) -> AudioApiResult<JsonResponse> {
translate::<JsonResponse, JsonResponseFormatter>(client, request_body).await
}
pub(crate) async fn translate_into_plain_text(
client: &Client,
request_body: TranslationsRequestBody,
) -> AudioApiResult<String> {
translate::<String, PlainTextResponseFormatter>(client, request_body).await
}
pub(crate) async fn translate_into_verbose_json(
client: &Client,
request_body: TranslationsRequestBody,
) -> AudioApiResult<VerboseJsonResponse> {
translate::<VerboseJsonResponse, VerboseJsonResponseFormatter>(
client,
request_body,
)
.await
}
pub(crate) async fn translate_into_srt(
client: &Client,
request_body: TranslationsRequestBody,
) -> AudioApiResult<SubRip> {
translate::<SubRip, SrtResponseFormatter>(client, request_body).await
}
pub(crate) async fn translate_into_vtt(
client: &Client,
request_body: TranslationsRequestBody,
) -> AudioApiResult<WebVtt> {
translate::<WebVtt, VttResponseFormatter>(client, request_body).await
}