eoka-protocol 0.6.2

Protocol and tool catalog definitions for eoka browser automation
Documentation
use serde::{Deserialize, Serialize};

pub const TURNSTILE_DEFAULT_TIMEOUT_MS: u64 = 30_000;
pub const TURNSTILE_MAX_TIMEOUT_MS: u64 = 120_000;

#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(default, deny_unknown_fields)]
pub struct CaptchaTurnstileArgs {
    #[serde(deserialize_with = "deserialize_timeout")]
    #[schemars(range(min = 1, max = TURNSTILE_MAX_TIMEOUT_MS))]
    pub timeout_ms: u64,
}

impl Default for CaptchaTurnstileArgs {
    fn default() -> Self {
        Self {
            timeout_ms: TURNSTILE_DEFAULT_TIMEOUT_MS,
        }
    }
}

fn deserialize_timeout<'de, D: serde::Deserializer<'de>>(d: D) -> Result<u64, D::Error> {
    let value = u64::deserialize(d)?;
    if !(1..=TURNSTILE_MAX_TIMEOUT_MS).contains(&value) {
        return Err(serde::de::Error::custom(
            "timeout_ms must be between 1 and 120000",
        ));
    }
    Ok(value)
}

#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct CaptchaTurnstileResult {
    pub tab_id: String,
    pub clicks: u32,
    pub elapsed_ms: u64,
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn timeout_defaults_and_is_bounded() {
        let args: CaptchaTurnstileArgs = serde_json::from_value(serde_json::json!({})).unwrap();
        assert_eq!(args.timeout_ms, TURNSTILE_DEFAULT_TIMEOUT_MS);
        for timeout in [0, TURNSTILE_MAX_TIMEOUT_MS + 1] {
            assert!(serde_json::from_value::<CaptchaTurnstileArgs>(
                serde_json::json!({ "timeout_ms": timeout })
            )
            .is_err());
        }
    }
}