use serde::{Deserialize, Serialize};
use serde_json::Value;
use crate::domain::JobKind;
use crate::error::JobError;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SendEmailPayload {
pub to: String,
pub subject: String,
pub body: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResizeImagePayload {
pub source_url: String,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SummarizeTextPayload {
pub text: String,
#[serde(default)]
pub max_words: Option<u32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WebhookDeliveryPayload {
pub url: String,
pub body: Value,
}
pub fn validate(kind: JobKind, payload: &Value) -> Result<(), JobError> {
fn check<T: for<'de> Deserialize<'de>>(kind: JobKind, payload: &Value) -> Result<(), JobError> {
serde_json::from_value::<T>(payload.clone())
.map(|_| ())
.map_err(|e| JobError::PayloadInvalid {
kind: kind.as_str(),
reason: e.to_string(),
})
}
match kind {
JobKind::SendEmail => check::<SendEmailPayload>(kind, payload),
JobKind::ResizeImage => check::<ResizeImagePayload>(kind, payload),
JobKind::SummarizeText => check::<SummarizeTextPayload>(kind, payload),
JobKind::WebhookDelivery => check::<WebhookDeliveryPayload>(kind, payload),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn send_email_valid() {
let p = json!({ "to": "a@b.c", "subject": "hi", "body": "hello" });
validate(JobKind::SendEmail, &p).unwrap();
}
#[test]
fn send_email_missing_field_fails() {
let p = json!({ "to": "a@b.c", "subject": "hi" });
let err = validate(JobKind::SendEmail, &p).unwrap_err();
assert_eq!(err.kind(), "payload_invalid");
}
#[test]
fn resize_image_valid() {
let p = json!({ "source_url": "https://x/y.png", "width": 100, "height": 100 });
validate(JobKind::ResizeImage, &p).unwrap();
}
#[test]
fn webhook_delivery_accepts_arbitrary_body() {
let p = json!({ "url": "https://x", "body": { "any": ["thing", 1, null] } });
validate(JobKind::WebhookDelivery, &p).unwrap();
}
}