use std::error::Error; use reqwest::header; use serde_json::Value; use crate::{error::Hyper3dError, response::parse_api_error_message, types::Hyper3dSettings}; pub(crate) async fn post_hyper3d_multipart( http_client: &reqwest::Client, settings: &Hyper3dSettings, path: &str, form: reqwest::multipart::Form, failure_context: &str, ) -> Result { let response = http_client .post(format!("{}{}", settings.base_url, path)) .header( header::AUTHORIZATION, format!("Bearer {}", settings.api_key), ) .header(header::ACCEPT, "application/json") .multipart(form) .send() .await .map_err(|error| map_reqwest_error(failure_context, path, error))?; parse_hyper3d_response(response, failure_context).await } pub(crate) async fn post_hyper3d_json( http_client: &reqwest::Client, settings: &Hyper3dSettings, path: &str, body: Value, failure_context: &str, ) -> Result { let response = http_client .post(format!("{}{}", settings.base_url, path)) .header( header::AUTHORIZATION, format!("Bearer {}", settings.api_key), ) .header(header::ACCEPT, "application/json") .header(header::CONTENT_TYPE, "application/json") .json(&body) .send() .await .map_err(|error| map_reqwest_error(failure_context, path, error))?; parse_hyper3d_response(response, failure_context).await } async fn parse_hyper3d_response( response: reqwest::Response, failure_context: &str, ) -> Result { let status = response.status(); let raw_text = response.text().await.map_err(|error| { Hyper3dError::request( format!("{failure_context}:读取上游响应失败:{error}"), None, false, false, false, true, None, None, ) })?; if !status.is_success() { return Err(Hyper3dError::upstream( parse_api_error_message(&raw_text, failure_context), status.as_u16(), truncate_raw(&raw_text), )); } serde_json::from_str::(&raw_text).map_err(|error| { Hyper3dError::response_parse( format!("{failure_context}:解析上游 JSON 失败:{error}"), truncate_raw(&raw_text), ) }) } fn map_reqwest_error(failure_context: &str, endpoint: &str, error: reqwest::Error) -> Hyper3dError { Hyper3dError::request( format!("{failure_context}:{error}"), Some(endpoint.to_string()), error.is_timeout(), error.is_connect(), error.is_request(), error.is_body(), error.status().map(|status| status.as_u16()), Error::source(&error).map(ToString::to_string), ) } fn truncate_raw(raw_text: &str) -> String { raw_text.chars().take(800).collect() }