use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct BaseResponse {
#[serde(rename = "code")]
pub code: String,
#[serde(rename = "errno")]
pub errno: i32,
#[serde(rename = "response")]
pub response: String,
#[serde(rename = "requestId")]
pub request_id: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct UploadOptions {
pub file_path: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct UploadSocialMediaOptions {
#[serde(rename = "socialLink")]
pub social_link: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SignedUrlResponse {
#[serde(rename = "code")]
pub code: String,
#[serde(rename = "errno")]
pub errno: i32,
#[serde(rename = "requestId")]
pub request_id: String,
#[serde(rename = "mediaId")]
pub media_id: String,
pub response: SignedUrlDetails,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct SignedUrlDetails {
#[serde(rename = "signedUrl")]
pub signed_url: String,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct UploadResult {
pub request_id: String,
#[serde(default)]
pub media_id: Option<String>,
#[serde(default)]
pub result_url: Option<String>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct GetResultOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub max_attempts: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub polling_interval: Option<u64>,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum FloatOrObject {
Float(f64),
Object(serde_json::Map<String, serde_json::Value>),
}
#[derive(Debug, Clone, Deserialize)]
pub struct DetectionModel {
pub name: String,
pub status: String,
#[serde(rename = "predictionNumber")]
pub prediction_number: Option<FloatOrObject>,
#[serde(rename = "normalizedPredictionNumber")]
pub normalized_prediction_number: Option<f64>,
#[serde(rename = "finalScore")]
pub final_score: Option<f64>,
#[serde(default)]
pub info: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct AnalysisResult {
#[serde(rename = "requestId")]
pub request_id: String,
#[serde(rename = "overallStatus")]
pub status: String,
#[serde(default)]
#[serde(rename = "finalScore")]
pub final_score: Option<f64>,
#[serde(default)]
pub models: Vec<DetectionModel>,
#[serde(default)]
pub info: Option<serde_json::Value>,
#[serde(default)]
#[serde(rename = "createdAt")]
pub created_at: Option<String>,
#[serde(default)]
#[serde(rename = "updatedAt")]
pub updated_at: Option<String>,
#[serde(default)]
#[serde(rename = "resultsSummary")]
pub results_summary: Option<ResultsSummary>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ResultsSummary {
pub status: String,
pub metadata: Option<serde_json::Map<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Default)]
pub struct BatchOptions {
pub max_concurrency: Option<usize>,
pub max_attempts: Option<u64>,
pub polling_interval: Option<u64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DetectionModelResult {
pub name: String,
pub status: String,
pub score: Option<f64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DetectionResult {
#[serde(rename = "requestId")]
pub request_id: String,
pub status: String,
pub score: Option<f64>,
pub models: Vec<DetectionModelResult>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct GetResultsOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub page_number: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub start_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub end_date: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_attempts: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub polling_interval: Option<u64>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct DetectionResultList {
#[serde(rename = "totalItems")]
pub total_items: u32,
#[serde(rename = "totalPages")]
pub total_pages: u32,
#[serde(rename = "currentPage")]
pub current_page: u32,
#[serde(rename = "currentPageItemsCount")]
pub current_page_items_count: u32,
#[serde(rename = "mediaList")]
pub items: Vec<AnalysisResult>,
}
#[derive(Debug, Clone)]
pub struct FormattedDetectionResultList {
pub total_items: u32,
pub total_pages: u32,
pub current_page: u32,
pub current_page_items_count: u32,
pub items: Vec<DetectionResult>,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::{json, Value};
#[test]
fn test_upload_options_serialization() {
let options = UploadOptions {
file_path: "path/to/file.jpg".to_string(),
};
let json_str = serde_json::to_string(&options).unwrap();
let json_value: Value = serde_json::from_str(&json_str).unwrap();
assert_eq!(json_value["file_path"], "path/to/file.jpg");
}
#[test]
fn test_upload_options_default_metadata() {
let options = UploadOptions {
file_path: "path/to/file.jpg".to_string(),
};
let json_str = serde_json::to_string(&options).unwrap();
let json_value: Value = serde_json::from_str(&json_str).unwrap();
assert_eq!(json_value["file_path"], "path/to/file.jpg");
}
#[test]
fn test_get_result_options_defaults() {
let options = GetResultOptions::default();
assert_eq!(options.max_attempts, None);
assert_eq!(options.polling_interval, None);
}
#[test]
fn test_get_result_options_serialization() {
let options = GetResultOptions {
max_attempts: Some(30),
polling_interval: Some(2000),
};
let json_str = serde_json::to_string(&options).unwrap();
let json_value: Value = serde_json::from_str(&json_str).unwrap();
assert_eq!(json_value["max_attempts"], 30);
assert_eq!(json_value["polling_interval"], 2000);
}
#[test]
fn test_batch_options_defaults() {
let options = BatchOptions::default();
assert_eq!(options.max_concurrency, None);
assert_eq!(options.max_attempts, None);
assert_eq!(options.polling_interval, None);
}
#[test]
fn test_detection_model_deserialization() {
let json_data = json!({
"name": "TestModel",
"status": "COMPLETED",
"predictionNumber": 92.5,
"normalizedPredictionNumber": 85.0,
"finalScore": 80.0,
"info": {
"confidence": "high",
"details": "Model specific details"
}
});
let model: DetectionModel = serde_json::from_value(json_data).unwrap();
assert_eq!(model.name, "TestModel");
assert_eq!(model.status, "COMPLETED");
assert_eq!(model.final_score, Some(80.0));
let info = model.info.unwrap();
assert_eq!(info["confidence"], "high");
assert_eq!(info["details"], "Model specific details");
}
#[test]
fn test_analysis_result_deserialization() {
let json_data = json!({
"requestId": "test-request-123",
"overallStatus": "COMPLETED",
"finalScore": 75,
"models": [
{
"name": "ModelA",
"status": "COMPLETED",
"finalScore": 80.0,
},
{
"name": "ModelB",
"status": "NOT_APPLICABLE"
},
{
"name": "ModelC",
"status": "NOT_APPLICABLE",
"predictionNumber": {
"reason": "relevance: no faces detected/faces too small",
"decision": "NOT_EVALUATED"
},
"normalizedPredictionNumber": null,
"rollingAvgNumber": null,
"finalScore": null
},
],
"info": {
"additionalInfo": "Test info"
},
"createdAt": "2023-01-01T12:00:00Z",
"updatedAt": "2023-01-01T12:05:00Z",
"resultsSummary": {
"status": "COMPLETED",
"metadata": {
"finalScore": 75,
"modelCount": 2
}
}
});
let result: AnalysisResult = serde_json::from_value(json_data).unwrap();
assert_eq!(result.request_id, "test-request-123");
assert_eq!(result.status, "COMPLETED");
assert_eq!(result.final_score, Some(75.0));
assert_eq!(result.models.len(), 3);
assert_eq!(result.models[0].name, "ModelA");
assert_eq!(result.models[0].status, "COMPLETED");
assert_eq!(result.models[0].final_score, Some(80.0));
assert_eq!(result.models[0].prediction_number, None);
assert_eq!(result.models[1].name, "ModelB");
assert_eq!(result.models[1].status, "NOT_APPLICABLE");
assert_eq!(result.models[1].final_score, None);
assert_eq!(result.models[1].prediction_number, None);
assert_eq!(result.models[2].name, "ModelC");
assert_eq!(result.models[2].status, "NOT_APPLICABLE");
assert_eq!(result.models[2].final_score, None);
assert!(matches!(
result.models[2].prediction_number,
Some(FloatOrObject::Object { .. })
));
assert_eq!(result.created_at, Some("2023-01-01T12:00:00Z".to_string()));
assert_eq!(result.updated_at, Some("2023-01-01T12:05:00Z".to_string()));
let results_summary = result.results_summary.unwrap();
assert_eq!(results_summary.status, "COMPLETED");
assert_eq!(results_summary.metadata.unwrap()["finalScore"], 75);
}
}