Skip to main content

eventuary_core/
organization.rs

1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::{Error, Result};
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
8#[serde(try_from = "String", into = "String")]
9pub struct OrganizationId(String);
10
11impl OrganizationId {
12    pub const PLATFORM: &'static str = "_platform";
13
14    pub fn new(s: impl Into<String>) -> Result<Self> {
15        let s = s.into();
16        if s.is_empty() {
17            return Err(Error::InvalidOrganization("must not be empty".into()));
18        }
19        if !s
20            .chars()
21            .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_' || c == '-')
22        {
23            return Err(Error::InvalidOrganization(format!("invalid: {s}")));
24        }
25        Ok(Self(s))
26    }
27
28    pub fn platform() -> Self {
29        Self(Self::PLATFORM.to_owned())
30    }
31
32    pub fn as_str(&self) -> &str {
33        &self.0
34    }
35}
36
37impl fmt::Display for OrganizationId {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        write!(f, "{}", self.0)
40    }
41}
42
43impl TryFrom<String> for OrganizationId {
44    type Error = Error;
45    fn try_from(s: String) -> Result<Self> {
46        Self::new(s)
47    }
48}
49
50impl From<OrganizationId> for String {
51    fn from(o: OrganizationId) -> Self {
52        o.0
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn platform_sentinel_is_constructible() {
62        let p = OrganizationId::platform();
63        assert_eq!(p.as_str(), "_platform");
64    }
65
66    #[test]
67    fn platform_sentinel_round_trips_through_validation() {
68        let parsed = OrganizationId::new("_platform").unwrap();
69        assert_eq!(parsed, OrganizationId::platform());
70    }
71}