Skip to main content

threads_rs/types/
ids.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::ops::Deref;
4
5macro_rules! id_newtype {
6    ($(#[$meta:meta])* $name:ident) => {
7        $(#[$meta])*
8        #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
9        #[serde(transparent)]
10        pub struct $name(pub String);
11
12        impl $name {
13            /// Returns `true` if the ID is non-empty.
14            pub fn is_valid(&self) -> bool {
15                !self.0.is_empty()
16            }
17        }
18
19        impl fmt::Display for $name {
20            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21                f.write_str(&self.0)
22            }
23        }
24
25        impl From<String> for $name {
26            fn from(s: String) -> Self {
27                Self(s)
28            }
29        }
30
31        impl From<&str> for $name {
32            fn from(s: &str) -> Self {
33                Self(s.to_owned())
34            }
35        }
36
37        impl AsRef<str> for $name {
38            fn as_ref(&self) -> &str {
39                &self.0
40            }
41        }
42
43        impl Deref for $name {
44            type Target = str;
45            fn deref(&self) -> &str {
46                &self.0
47            }
48        }
49    };
50}
51
52id_newtype!(
53    /// Unique identifier for a post.
54    PostId
55);
56
57id_newtype!(
58    /// Unique identifier for a user.
59    UserId
60);
61
62id_newtype!(
63    /// Unique identifier for a media container.
64    ContainerId
65);
66
67id_newtype!(
68    /// Unique identifier for a location.
69    LocationId
70);
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn test_post_id_valid() {
78        let id = PostId::from("12345");
79        assert!(id.is_valid());
80        assert_eq!(id.to_string(), "12345");
81    }
82
83    #[test]
84    fn test_post_id_invalid() {
85        let id = PostId::from("");
86        assert!(!id.is_valid());
87    }
88
89    #[test]
90    fn test_user_id_from_string() {
91        let id = UserId::from(String::from("user-1"));
92        assert_eq!(id.as_ref(), "user-1");
93        assert_eq!(&*id, "user-1");
94    }
95
96    #[test]
97    fn test_id_serde_roundtrip() {
98        let id = ContainerId::from("c-99");
99        let json = serde_json::to_string(&id).unwrap();
100        assert_eq!(json, r#""c-99""#);
101        let back: ContainerId = serde_json::from_str(&json).unwrap();
102        assert_eq!(back, id);
103    }
104
105    #[test]
106    fn test_location_id_deref() {
107        let id = LocationId::from("loc-42");
108        // Deref to str allows str methods directly
109        assert!(id.starts_with("loc-"));
110    }
111}