mas_config/sections/
captcha.rs1use schemars::JsonSchema;
8use serde::{Deserialize, Serialize, de::Error};
9
10use crate::ConfigurationSection;
11
12#[derive(Clone, Copy, Debug, Deserialize, JsonSchema, Serialize)]
14pub enum CaptchaServiceKind {
15 #[serde(rename = "recaptcha_v2")]
17 RecaptchaV2,
18
19 #[serde(rename = "cloudflare_turnstile")]
21 CloudflareTurnstile,
22
23 #[serde(rename = "hcaptcha")]
25 HCaptcha,
26}
27
28#[derive(Clone, Debug, Deserialize, JsonSchema, Serialize, Default)]
30pub struct CaptchaConfig {
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub service: Option<CaptchaServiceKind>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub site_key: Option<String>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub secret_key: Option<String>,
42}
43
44impl CaptchaConfig {
45 pub(crate) fn is_default(&self) -> bool {
47 self.service.is_none() && self.site_key.is_none() && self.secret_key.is_none()
48 }
49}
50
51impl ConfigurationSection for CaptchaConfig {
52 const PATH: Option<&'static str> = Some("captcha");
53
54 fn validate(
55 &self,
56 figment: &figment::Figment,
57 ) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
58 let metadata = figment.find_metadata(Self::PATH.unwrap());
59
60 let error_on_field = |mut error: figment::error::Error, field: &'static str| {
61 error.metadata = metadata.cloned();
62 error.profile = Some(figment::Profile::Default);
63 error.path = vec![Self::PATH.unwrap().to_owned(), field.to_owned()];
64 error
65 };
66
67 let missing_field = |field: &'static str| {
68 error_on_field(figment::error::Error::missing_field(field), field)
69 };
70
71 if let Some(CaptchaServiceKind::RecaptchaV2) = self.service {
72 if self.site_key.is_none() {
73 return Err(missing_field("site_key").into());
74 }
75
76 if self.secret_key.is_none() {
77 return Err(missing_field("secret_key").into());
78 }
79 }
80
81 Ok(())
82 }
83}