klick_domain/
project.rs

1use std::str::FromStr;
2
3use thiserror::Error;
4use time::OffsetDateTime;
5use uuid::Uuid;
6
7#[derive(Debug, Clone)]
8pub struct Project<D> {
9    pub id: Id,
10    pub created_at: OffsetDateTime,
11    pub modified_at: Option<OffsetDateTime>,
12    pub data: D,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Hash)]
16pub struct Id(Uuid);
17
18impl Id {
19    #[must_use]
20    #[allow(clippy::new_without_default)]
21    pub fn new() -> Self {
22        Self(Uuid::new_v4())
23    }
24
25    #[must_use]
26    pub const fn from_uuid(uuid: Uuid) -> Self {
27        Self(uuid)
28    }
29
30    #[must_use]
31    pub const fn to_uuid(&self) -> Uuid {
32        self.0
33    }
34}
35
36impl ToString for Id {
37    fn to_string(&self) -> String {
38        self.0.simple().to_string()
39    }
40}
41
42#[derive(Debug, Error)]
43#[error("Invalid project ID")]
44pub struct IdParseError;
45
46impl FromStr for Id {
47    type Err = IdParseError;
48    fn from_str(from: &str) -> Result<Self, Self::Err> {
49        let uuid = from.parse::<Uuid>().map_err(|_| IdParseError)?;
50        Ok(Self(uuid))
51    }
52}
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use uuid::uuid;
58
59    const EXAMPLE_ID: Uuid = uuid!("157296c3-4a0c-4794-91bb-34008da55535");
60
61    #[test]
62    fn id_to_string() {
63        let id = Id::from_uuid(EXAMPLE_ID);
64        assert_eq!(id.to_string(), "157296c34a0c479491bb34008da55535");
65    }
66
67    #[test]
68    fn parse() {
69        let id = "157296c3-4a0c-4794-91bb-34008da55535"
70            .parse::<Id>()
71            .unwrap();
72        assert_eq!(id.0, EXAMPLE_ID);
73
74        let id = "157296c34a0c479491bb34008da55535".parse::<Id>().unwrap();
75        assert_eq!(id.0, EXAMPLE_ID);
76
77        let id = "157296C34A0C479491BB34008DA55535".parse::<Id>().unwrap();
78        assert_eq!(id.0, EXAMPLE_ID);
79    }
80}