lambda_appsync/
id.rs

1use std::ops::Deref;
2
3use serde::{Deserialize, Serialize};
4
5/// A custom UUID-based identifier type for AppSync GraphQL objects.
6///
7/// This type wraps a [Uuid](uuid::Uuid) (v4) to provide a standardized way of identifying objects
8/// in AppSync while ensuring type safety and validation. It implements serialization
9/// and deserialization as [String] as expected by GraphQL.
10///
11/// # Example
12/// ```
13/// use lambda_appsync::ID;
14///
15/// let id = ID::new();
16/// let id_str: String = id.into();
17/// ```
18#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
19#[serde(try_from = "String", into = "String")]
20pub struct ID(uuid::Uuid);
21impl ID {
22    /// Create a new random ID based on the UUIDv4 specification.
23    ///
24    /// # Example
25    /// ```
26    /// use lambda_appsync::ID;
27    ///
28    /// let id = ID::new();
29    /// ```
30    pub fn new() -> Self {
31        Self(uuid::Uuid::new_v4())
32    }
33}
34impl Default for ID {
35    fn default() -> Self {
36        Self::new()
37    }
38}
39impl TryFrom<String> for ID {
40    type Error = uuid::Error;
41    /// Attempts to create an ID from a string representation of a UUID.
42    ///
43    /// # Example
44    /// ```
45    /// use lambda_appsync::ID;
46    ///
47    /// let id = ID::try_from("123e4567-e89b-12d3-a456-426614174000".to_string()).unwrap();
48    /// ```
49    ///
50    /// # Errors
51    /// Returns a `uuid::Error` if:
52    /// - The string is not a valid UUID format
53    /// - The string contains invalid characters
54    /// - The string is not of the correct length
55    fn try_from(value: String) -> Result<Self, Self::Error> {
56        Ok(ID(uuid::Uuid::parse_str(&value)?))
57    }
58}
59impl core::fmt::Display for ID {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        write!(f, "{}", self.0)
62    }
63}
64impl From<ID> for String {
65    fn from(value: ID) -> Self {
66        value.to_string()
67    }
68}
69impl Deref for ID {
70    type Target = uuid::Uuid;
71
72    fn deref(&self) -> &Self::Target {
73        &self.0
74    }
75}
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_id_creation() {
82        let id = ID::new();
83        let id_str: String = id.into();
84
85        // Test parsing back
86        let parsed = ID::try_from(id_str.clone()).unwrap();
87        assert_eq!(id, parsed);
88
89        // Test display
90        assert_eq!(id.to_string(), id_str);
91    }
92
93    #[test]
94    fn test_new_id() {
95        let id = ID::new();
96        assert!(uuid::Uuid::parse_str(&id.to_string()).is_ok());
97    }
98
99    #[test]
100    fn test_id_conversion() {
101        let id = ID::new();
102        let id_string = String::from(id);
103        let converted_id = ID::try_from(id_string.clone()).unwrap();
104        assert_eq!(id, converted_id);
105        assert_eq!(id.to_string(), id_string);
106    }
107
108    #[test]
109    fn test_invalid_id() {
110        let result = ID::try_from("not-a-uuid".to_string());
111        assert!(result.is_err());
112    }
113
114    #[test]
115    fn test_id_deref() {
116        let id = ID::new();
117        let uuid: &uuid::Uuid = id.deref();
118        assert_eq!(uuid.to_string(), id.to_string());
119    }
120
121    #[test]
122    fn test_id_display() {
123        let id = ID::new();
124        let uuid_string = id.0.to_string();
125        assert_eq!(id.to_string(), uuid_string);
126    }
127}