Skip to main content

im_core/site/
dto.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
4pub struct SiteDomain(String);
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
7pub struct SiteRootDraft {
8    pub domain: SiteDomain,
9    pub body: String,
10}
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
13pub struct SiteRootDocument {
14    pub domain: SiteDomain,
15    pub body: Option<String>,
16    pub raw: serde_json::Value,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct SitePageRef {
21    pub domain: SiteDomain,
22    pub slug: crate::content::PageSlug,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
26pub struct SitePageQuery {
27    pub domain: SiteDomain,
28    pub limit: crate::ids::PageLimit,
29    pub cursor: Option<crate::ids::Cursor>,
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
33pub struct SitePageDraft {
34    pub domain: SiteDomain,
35    pub slug: crate::content::PageSlug,
36    pub body: String,
37}
38
39#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
40pub struct SitePageUpdate {
41    pub body: String,
42}
43
44#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
45pub struct SitePageDocument {
46    pub domain: SiteDomain,
47    pub slug: crate::content::PageSlug,
48    pub body: Option<String>,
49    pub raw: serde_json::Value,
50}
51
52impl SiteDomain {
53    pub fn parse(input: impl AsRef<str>) -> crate::ImResult<Self> {
54        let normalized = input
55            .as_ref()
56            .trim()
57            .to_ascii_lowercase()
58            .trim_end_matches('.')
59            .to_string();
60        if normalized.is_empty() {
61            return Err(crate::ImError::invalid_input(
62                Some("domain".to_string()),
63                "did_domain is required",
64            ));
65        }
66        if normalized.contains("://") {
67            return Err(crate::ImError::invalid_input(
68                Some("domain".to_string()),
69                "did_domain must be a bare domain without a URL scheme",
70            ));
71        }
72        if normalized.contains(['/', '?', '#']) {
73            return Err(crate::ImError::invalid_input(
74                Some("domain".to_string()),
75                "did_domain must not include a path, query, or fragment",
76            ));
77        }
78        if normalized.contains(':') {
79            return Err(crate::ImError::invalid_input(
80                Some("domain".to_string()),
81                "did_domain must not include a port",
82            ));
83        }
84        if normalized.chars().any(char::is_whitespace) {
85            return Err(crate::ImError::invalid_input(
86                Some("domain".to_string()),
87                "did_domain must not contain whitespace",
88            ));
89        }
90        if normalized.contains('@') || normalized.contains('%') {
91            return Err(crate::ImError::invalid_input(
92                Some("domain".to_string()),
93                "did_domain must be a bare domain",
94            ));
95        }
96        Ok(Self(normalized))
97    }
98
99    pub fn as_str(&self) -> &str {
100        &self.0
101    }
102}
103
104impl SitePageRef {
105    pub fn new(domain: SiteDomain, slug: crate::content::PageSlug) -> Self {
106        Self { domain, slug }
107    }
108}
109
110impl Default for SitePageQuery {
111    fn default() -> Self {
112        Self {
113            domain: SiteDomain(String::new()),
114            limit: crate::ids::PageLimit(50),
115            cursor: None,
116        }
117    }
118}