use axum::{
http::StatusCode,
response::{IntoResponse, Response},
Json,
};
use serde::{Deserialize, Serialize};
use super::jobs::JobStatus;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextResponse {
pub request_id: String,
pub text: String,
pub confidence: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub latex: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mathml: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PdfResponse {
pub pdf_id: String,
pub status: JobStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub result: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorResponse {
pub error_code: String,
pub message: String,
#[serde(skip)]
pub status: StatusCode,
}
impl ErrorResponse {
pub fn validation_error(message: impl Into<String>) -> Self {
Self {
error_code: "VALIDATION_ERROR".to_string(),
message: message.into(),
status: StatusCode::BAD_REQUEST,
}
}
pub fn unauthorized(message: impl Into<String>) -> Self {
Self {
error_code: "UNAUTHORIZED".to_string(),
message: message.into(),
status: StatusCode::UNAUTHORIZED,
}
}
pub fn not_found(message: impl Into<String>) -> Self {
Self {
error_code: "NOT_FOUND".to_string(),
message: message.into(),
status: StatusCode::NOT_FOUND,
}
}
pub fn rate_limited(message: impl Into<String>) -> Self {
Self {
error_code: "RATE_LIMIT_EXCEEDED".to_string(),
message: message.into(),
status: StatusCode::TOO_MANY_REQUESTS,
}
}
pub fn internal_error(message: impl Into<String>) -> Self {
Self {
error_code: "INTERNAL_ERROR".to_string(),
message: message.into(),
status: StatusCode::INTERNAL_SERVER_ERROR,
}
}
pub fn service_unavailable(message: impl Into<String>) -> Self {
Self {
error_code: "SERVICE_UNAVAILABLE".to_string(),
message: message.into(),
status: StatusCode::SERVICE_UNAVAILABLE,
}
}
pub fn not_implemented(message: impl Into<String>) -> Self {
Self {
error_code: "NOT_IMPLEMENTED".to_string(),
message: message.into(),
status: StatusCode::NOT_IMPLEMENTED,
}
}
}
impl IntoResponse for ErrorResponse {
fn into_response(self) -> Response {
let status = self.status;
(status, Json(self)).into_response()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_text_response_serialization() {
let response = TextResponse {
request_id: "test-123".to_string(),
text: "Hello World".to_string(),
confidence: 0.95,
latex: Some("x^2".to_string()),
mathml: None,
html: None,
};
let json = serde_json::to_string(&response).unwrap();
assert!(json.contains("request_id"));
assert!(json.contains("test-123"));
assert!(!json.contains("mathml"));
}
#[test]
fn test_error_response_creation() {
let error = ErrorResponse::validation_error("Invalid input");
assert_eq!(error.status, StatusCode::BAD_REQUEST);
assert_eq!(error.error_code, "VALIDATION_ERROR");
let error = ErrorResponse::unauthorized("Invalid credentials");
assert_eq!(error.status, StatusCode::UNAUTHORIZED);
let error = ErrorResponse::rate_limited("Too many requests");
assert_eq!(error.status, StatusCode::TOO_MANY_REQUESTS);
}
}