use serde::Serialize;
use super::complex_image::timeouts;
use super::CaptchaRequest;
use crate::error::{Error, Result};
use crate::timeouts::GetResultTimeouts;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub enum PayloadType {
Audio,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RecognitionMetadata {
#[serde(rename = "Task")]
pub task: String,
#[serde(rename = "TaskArgument", skip_serializing_if = "Option::is_none")]
pub task_argument: Option<String>,
#[serde(rename = "PayloadType", skip_serializing_if = "Option::is_none")]
pub payload_type: Option<PayloadType>,
}
impl RecognitionMetadata {
pub fn new(task: impl Into<String>) -> Self {
Self {
task: task.into(),
task_argument: None,
payload_type: None,
}
}
pub fn with_task_argument(mut self, value: impl Into<String>) -> Self {
self.task_argument = Some(value.into());
self
}
pub fn with_payload_type(mut self, value: PayloadType) -> Self {
self.payload_type = Some(value);
self
}
}
#[derive(Debug, Clone, Serialize)]
pub struct RecognitionComplexImageTaskRequest {
#[serde(rename = "imagesBase64")]
pub images_base64: Vec<String>,
pub metadata: RecognitionMetadata,
#[serde(rename = "userAgent", skip_serializing_if = "Option::is_none")]
pub user_agent: Option<String>,
}
impl RecognitionComplexImageTaskRequest {
pub fn new(images_base64: Vec<String>, metadata: RecognitionMetadata) -> Self {
Self {
images_base64,
metadata,
user_agent: None,
}
}
pub fn single(image_base64: impl Into<String>, metadata: RecognitionMetadata) -> Self {
Self::new(vec![image_base64.into()], metadata)
}
pub fn with_user_agent(mut self, value: impl Into<String>) -> Self {
self.user_agent = Some(value.into());
self
}
}
impl CaptchaRequest for RecognitionComplexImageTaskRequest {
const TASK_TYPE: &'static str = "ComplexImageTask";
const CLASS: Option<&'static str> = Some("recognition");
fn timeouts(&self) -> GetResultTimeouts {
timeouts()
}
fn validate(&self) -> Result<()> {
if self.metadata.task.is_empty() {
return Err(Error::Validation("metadata: Task must be defined".into()));
}
if self.images_base64.is_empty() {
return Err(Error::Validation(
"images_base64: at least one image expected".into(),
));
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn minimal_task() {
let task =
RecognitionComplexImageTaskRequest::single("AAA", RecognitionMetadata::new("shein"))
.to_task()
.unwrap();
assert_eq!(
task,
json!({"type": "ComplexImageTask", "class": "recognition",
"imagesBase64": ["AAA"], "metadata": {"Task": "shein"}})
);
}
#[test]
fn full_task() {
let meta = RecognitionMetadata::new("bills_audio")
.with_task_argument("123")
.with_payload_type(PayloadType::Audio);
let task = RecognitionComplexImageTaskRequest::new(vec!["A".into(), "B".into()], meta)
.with_user_agent("UA")
.to_task()
.unwrap();
assert_eq!(
task,
json!({"type": "ComplexImageTask", "class": "recognition",
"imagesBase64": ["A", "B"],
"metadata": {"Task": "bills_audio", "TaskArgument": "123", "PayloadType": "Audio"},
"userAgent": "UA"})
);
}
#[test]
fn validation_errors() {
let no_images =
RecognitionComplexImageTaskRequest::new(vec![], RecognitionMetadata::new("shein"));
assert!(matches!(no_images.to_task(), Err(Error::Validation(_))));
let no_task = RecognitionComplexImageTaskRequest::single("A", RecognitionMetadata::new(""));
assert!(matches!(no_task.to_task(), Err(Error::Validation(_))));
}
}