Skip to main content

captcha_sdk/
token.rs

1use std::{error::Error, fmt};
2
3use zeroize::Zeroizing;
4
5/// Maximum accepted CAPTCHA token length, in bytes.
6pub const MAX_CAPTCHA_TOKEN_LEN: usize = 16 * 1024;
7
8/// An opaque token produced by a CAPTCHA provider's client-side widget.
9///
10/// The token is intentionally not serializable and its [`Debug`](fmt::Debug)
11/// output is redacted.
12pub struct CaptchaToken(Zeroizing<String>);
13
14impl CaptchaToken {
15    /// Validates and stores a non-empty CAPTCHA token.
16    ///
17    /// # Errors
18    ///
19    /// Returns [`CaptchaTokenError`] when the token is empty or exceeds
20    /// [`MAX_CAPTCHA_TOKEN_LEN`].
21    pub fn new(value: impl Into<String>) -> Result<Self, CaptchaTokenError> {
22        let value = value.into();
23        if value.is_empty() {
24            return Err(CaptchaTokenError::Empty);
25        }
26        if value.len() > MAX_CAPTCHA_TOKEN_LEN {
27            return Err(CaptchaTokenError::TooLong);
28        }
29        Ok(Self(Zeroizing::new(value)))
30    }
31
32    /// Exposes the token only to provider adapters while constructing a request.
33    #[allow(dead_code)]
34    pub(crate) fn expose(&self) -> &str {
35        self.0.as_str()
36    }
37}
38
39impl fmt::Debug for CaptchaToken {
40    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
41        formatter.write_str("CaptchaToken([REDACTED])")
42    }
43}
44
45/// Reason a CAPTCHA token could not be accepted.
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47#[non_exhaustive]
48pub enum CaptchaTokenError {
49    /// The token was empty.
50    Empty,
51    /// The token exceeded [`MAX_CAPTCHA_TOKEN_LEN`].
52    TooLong,
53}
54
55impl fmt::Display for CaptchaTokenError {
56    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
57        let message = match self {
58            Self::Empty => "CAPTCHA token must not be empty",
59            Self::TooLong => "CAPTCHA token is too long",
60        };
61        formatter.write_str(message)
62    }
63}
64
65impl Error for CaptchaTokenError {}