Skip to main content

scalar_cms/
types.rs

1use std::ops::{Deref, DerefMut};
2
3use scalar_expr::expression;
4use serde::{Deserialize, Serialize};
5
6use crate::{
7    db::{ContentActions, ValidationContext},
8    editor_field::ToEditorField,
9    validations::{Validate, ValidationError},
10    DatabaseConnection, Document,
11};
12
13macro_rules! deref {
14    ($ty:ty > $target:ty) => {
15        impl Deref for $ty {
16            type Target = $target;
17
18            fn deref(&self) -> &Self::Target {
19                &self.0
20            }
21        }
22        impl DerefMut for $ty {
23            fn deref_mut(&mut self) -> &mut Self::Target {
24                &mut self.0
25            }
26        }
27    };
28
29    (generic $ty:ident > $target:ty) => {
30        impl<T: ToEditorField> Deref for $ty<T> {
31            type Target = $target;
32
33            fn deref(&self) -> &Self::Target {
34                &self.0
35            }
36        }
37        impl<T: ToEditorField> DerefMut for $ty<T> {
38            fn deref_mut(&mut self) -> &mut Self::Target {
39                &mut self.0
40            }
41        }
42    };
43}
44
45#[derive(Serialize, Deserialize, Debug)]
46pub struct MultiLine(pub String);
47
48deref!(MultiLine > str);
49
50#[derive(Serialize, Deserialize, Debug)]
51#[serde(transparent)]
52pub struct Markdown(pub String);
53
54deref!(Markdown > str);
55
56#[derive(Serialize, Deserialize, Debug)]
57#[serde(transparent)]
58pub struct Slug(pub String);
59
60deref!(Slug > str);
61
62impl Validate for Slug {
63    async fn validate<
64        DB: DatabaseConnection + ContentActions<D> + Send + Sync,
65        D: Document + Send + Sync,
66    >(
67        &self,
68        ctx: ValidationContext<'_, DB, D>,
69    ) -> Result<(), crate::validations::ValidationError> {
70        self.0
71            .chars()
72            .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
73            .then_some(())
74            .ok_or_else(|| {
75                ValidationError::Single(
76                    "slugs can only contain alphanumeic characters, -, and _.".into(),
77                )
78            })?;
79        ctx.none(expression!($current == self.0))
80            .await
81            .unwrap()
82            .then_some(())
83            .ok_or_else(|| ValidationError::Single("slugs must be unique!".into()))
84    }
85}
86
87#[derive(Serialize, Deserialize, Debug)]
88#[serde(from = "Option<T>")]
89pub struct Toggle<T: ToEditorField>(pub Option<T>);
90
91impl<T: ToEditorField> From<Option<T>> for Toggle<T> {
92    fn from(value: Option<T>) -> Self {
93        Self(value)
94    }
95}
96
97impl<T: ToEditorField> Default for Toggle<T> {
98    fn default() -> Self {
99        Self(Option::default())
100    }
101}
102
103deref!(generic Toggle > Option<T>);