use serde::{Deserialize, Serialize};
use validator::Validate;
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct TextRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub src: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base64: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(url)]
pub url: Option<String>,
#[serde(default)]
pub metadata: RequestMetadata,
}
impl TextRequest {
pub async fn get_image_data(&self) -> anyhow::Result<Vec<u8>> {
if let Some(base64_data) = &self.base64 {
use base64::Engine;
let decoded = base64::engine::general_purpose::STANDARD.decode(base64_data)?;
Ok(decoded)
} else if let Some(url) = &self.url {
let response = reqwest::get(url).await?;
let bytes = response.bytes().await?;
Ok(bytes.to_vec())
} else {
anyhow::bail!("No image data provided")
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct StrokesRequest {
#[validate(length(min = 1))]
pub strokes: Vec<Stroke>,
#[serde(default)]
pub metadata: RequestMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Stroke {
pub x: Vec<f64>,
pub y: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub t: Option<Vec<f64>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct LatexRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub src: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub base64: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(url)]
pub url: Option<String>,
#[serde(default)]
pub metadata: RequestMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, Validate)]
pub struct PdfRequest {
#[validate(url)]
pub url: String,
#[serde(default)]
pub options: PdfOptions,
#[serde(skip_serializing_if = "Option::is_none")]
#[validate(url)]
pub webhook_url: Option<String>,
#[serde(default)]
pub metadata: RequestMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PdfOptions {
#[serde(default = "default_format")]
pub format: String,
#[serde(default)]
pub enable_ocr: bool,
#[serde(default = "default_true")]
pub include_images: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_range: Option<String>,
}
fn default_format() -> String {
"mmd".to_string()
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct RequestMetadata {
#[serde(default = "default_formats")]
pub formats: Vec<String>,
#[serde(default)]
pub include_confidence: bool,
#[serde(default = "default_true")]
pub enable_math: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub language: Option<String>,
}
fn default_formats() -> Vec<String> {
vec!["text".to_string()]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_text_request_validation() {
let request = TextRequest {
src: None,
base64: Some("SGVsbG8gV29ybGQ=".to_string()),
url: None,
metadata: RequestMetadata::default(),
};
assert!(request.validate().is_ok());
}
#[test]
fn test_strokes_request_validation() {
let request = StrokesRequest {
strokes: vec![Stroke {
x: vec![0.0, 1.0, 2.0],
y: vec![0.0, 1.0, 0.0],
t: None,
}],
metadata: RequestMetadata::default(),
};
assert!(request.validate().is_ok());
}
#[test]
fn test_empty_strokes_validation() {
let request = StrokesRequest {
strokes: vec![],
metadata: RequestMetadata::default(),
};
assert!(request.validate().is_err());
}
#[test]
fn test_pdf_request_validation() {
let request = PdfRequest {
url: "https://example.com/document.pdf".to_string(),
options: PdfOptions::default(),
webhook_url: None,
metadata: RequestMetadata::default(),
};
assert!(request.validate().is_ok());
}
#[test]
fn test_invalid_url() {
let request = PdfRequest {
url: "not-a-url".to_string(),
options: PdfOptions::default(),
webhook_url: None,
metadata: RequestMetadata::default(),
};
assert!(request.validate().is_err());
}
}