use crate::Captcha;
use crate::Error;
use crate::Secret;
#[cfg_attr(docsrs, allow(rustdoc::missing_doc_code_examples))]
#[derive(Debug, Default, serde::Serialize)]
pub struct Request {
captcha: Captcha,
secret: Secret,
}
#[cfg_attr(docsrs, allow(rustdoc::missing_doc_code_examples))]
impl Request {
#[allow(dead_code)]
#[cfg_attr(
feature = "trace",
tracing::instrument(
name = "Create new Request from Captcha struct.",
skip(secret),
level = "debug"
)
)]
pub fn new(secret: &str, captcha: Captcha) -> Result<Request, Error> {
Ok(Request {
captcha,
secret: Secret::parse(secret.to_owned())?,
})
}
#[allow(dead_code)]
#[cfg_attr(
feature = "trace",
tracing::instrument(
name = "Create new Request from response string.",
skip(secret),
level = "debug"
)
)]
pub fn new_from_response(secret: &str, response: &str) -> Result<Request, Error> {
let captcha = Captcha::new(response)?;
Request::new(secret, captcha)
}
#[allow(dead_code)]
#[cfg_attr(
feature = "trace",
tracing::instrument(
name = "Request verification from captval.",
skip(self),
fields(captcha = ?self.captcha),
level = "debug"
)
)]
pub fn set_remoteip(mut self, remoteip: &str) -> Result<Self, Error> {
self.captcha.set_remoteip(remoteip)?;
Ok(self)
}
#[cfg_attr(
feature = "trace",
tracing::instrument(
name = "Request verification from captval.",
skip(self),
fields(captcha = ?self.captcha),
level = "debug"
)
)]
pub fn set_sitekey(mut self, sitekey: &str) -> Result<Self, Error> {
self.captcha.set_sitekey(sitekey)?;
Ok(self)
}
#[allow(dead_code)]
pub(crate) fn secret(&self) -> Secret {
self.secret.clone()
}
#[allow(dead_code)]
pub(crate) fn captcha(&self) -> Captcha {
self.captcha.clone()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Captcha;
use claims::{assert_none, assert_ok};
use rand::distr::Alphanumeric;
use rand::{rng, Rng};
use std::iter;
fn random_hex_string(len: usize) -> String {
let mut rng = rng();
let chars: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.map(char::from)
.take(len / 2)
.collect();
hex::encode(chars)
}
fn random_response() -> String {
let mut rng = rng();
iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.map(char::from)
.take(100)
.collect()
}
fn dummy_captcha() -> Captcha {
Captcha::new(&random_response())
.unwrap()
.set_remoteip(&mockd::internet::ipv4_address())
.unwrap()
.set_sitekey(&mockd::unique::uuid_v4())
.unwrap()
}
#[test]
fn valid_new_from_captcha_struct() {
let secret = format!("0x{}", random_hex_string(40));
let captcha = dummy_captcha();
assert_ok!(Request::new(&secret, captcha));
}
#[test]
fn valid_new_from_response() {
let secret = format!("0x{}", random_hex_string(40));
let response = random_response();
let request = Request::new_from_response(&secret, &response).unwrap();
assert_eq!(&secret, &request.secret().to_string().as_str());
let Captcha {
response: resp,
remoteip: ip,
sitekey: key,
} = request.captcha;
assert_eq!(response, resp.to_string());
assert_none!(ip);
assert_none!(key);
}
}