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>);