mas_templates/context/
captcha.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use std::sync::Arc;
8
9use mas_i18n::DataLocale;
10use minijinja::{
11    Value,
12    value::{Enumerator, Object},
13};
14use serde::Serialize;
15
16use crate::TemplateContext;
17
18#[derive(Debug)]
19struct CaptchaConfig(mas_data_model::CaptchaConfig);
20
21impl Object for CaptchaConfig {
22    fn get_value(self: &Arc<Self>, key: &Value) -> Option<Value> {
23        match key.as_str() {
24            Some("service") => Some(match &self.0.service {
25                mas_data_model::CaptchaService::RecaptchaV2 => "recaptcha_v2".into(),
26                mas_data_model::CaptchaService::CloudflareTurnstile => {
27                    "cloudflare_turnstile".into()
28                }
29                mas_data_model::CaptchaService::HCaptcha => "hcaptcha".into(),
30            }),
31            Some("site_key") => Some(self.0.site_key.clone().into()),
32            _ => None,
33        }
34    }
35
36    fn enumerate(self: &Arc<Self>) -> Enumerator {
37        Enumerator::Str(&["service", "site_key"])
38    }
39}
40
41/// Context with an optional CAPTCHA configuration in it
42#[derive(Serialize)]
43pub struct WithCaptcha<T> {
44    captcha: Option<Value>,
45
46    #[serde(flatten)]
47    inner: T,
48}
49
50impl<T> WithCaptcha<T> {
51    #[must_use]
52    pub(crate) fn new(captcha: Option<mas_data_model::CaptchaConfig>, inner: T) -> Self {
53        Self {
54            captcha: captcha.map(|captcha| Value::from_object(CaptchaConfig(captcha))),
55            inner,
56        }
57    }
58}
59
60impl<T: TemplateContext> TemplateContext for WithCaptcha<T> {
61    fn sample(
62        now: chrono::DateTime<chrono::prelude::Utc>,
63        rng: &mut impl rand::prelude::Rng,
64        locales: &[DataLocale],
65    ) -> Vec<Self>
66    where
67        Self: Sized,
68    {
69        let inner = T::sample(now, rng, locales);
70        inner
71            .into_iter()
72            .map(|inner| Self::new(None, inner))
73            .collect()
74    }
75}