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 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 PostId
55);
56
57id_newtype!(
58 UserId
60);
61
62id_newtype!(
63 ContainerId
65);
66
67id_newtype!(
68 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 assert!(id.starts_with("loc-"));
110 }
111}