elif_orm/model/
primary_key.rs

1//! Primary Key System - Types and implementations for model primary keys
2//!
3//! Supports integer, UUID, and composite primary keys with proper serialization,
4//! display formatting, and type conversion utilities.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use uuid::Uuid;
9
10/// Primary key types supported by the ORM
11#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
12pub enum PrimaryKey {
13    /// Auto-incrementing integer primary key
14    Integer(i64),
15    /// UUID primary key
16    Uuid(Uuid),
17    /// Composite primary key (multiple fields)
18    Composite(HashMap<String, String>),
19}
20
21impl std::fmt::Display for PrimaryKey {
22    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
23        match self {
24            PrimaryKey::Integer(id) => write!(f, "{}", id),
25            PrimaryKey::Uuid(id) => write!(f, "{}", id),
26            PrimaryKey::Composite(fields) => {
27                let pairs: Vec<String> =
28                    fields.iter().map(|(k, v)| format!("{}:{}", k, v)).collect();
29                write!(f, "{}", pairs.join(","))
30            }
31        }
32    }
33}
34
35impl Default for PrimaryKey {
36    fn default() -> Self {
37        PrimaryKey::Integer(0)
38    }
39}
40
41impl PrimaryKey {
42    /// Extract as i64 if this is an Integer primary key
43    pub fn as_i64(&self) -> Option<i64> {
44        match self {
45            PrimaryKey::Integer(id) => Some(*id),
46            _ => None,
47        }
48    }
49
50    /// Extract as UUID if this is a UUID primary key
51    pub fn as_uuid(&self) -> Option<Uuid> {
52        match self {
53            PrimaryKey::Uuid(id) => Some(*id),
54            _ => None,
55        }
56    }
57
58    /// Extract as composite fields if this is a Composite primary key
59    pub fn as_composite(&self) -> Option<&HashMap<String, String>> {
60        match self {
61            PrimaryKey::Composite(fields) => Some(fields),
62            _ => None,
63        }
64    }
65
66    /// Check if this is a valid (non-default) primary key
67    pub fn is_valid(&self) -> bool {
68        match self {
69            PrimaryKey::Integer(0) => false,
70            PrimaryKey::Integer(_) => true,
71            PrimaryKey::Uuid(uuid) => !uuid.is_nil(),
72            PrimaryKey::Composite(fields) => !fields.is_empty(),
73        }
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80
81    #[test]
82    fn test_primary_key_display() {
83        let int_key = PrimaryKey::Integer(123);
84        assert_eq!(format!("{}", int_key), "123");
85
86        let uuid_key =
87            PrimaryKey::Uuid(Uuid::parse_str("550e8400-e29b-41d4-a716-446655440000").unwrap());
88        assert_eq!(
89            format!("{}", uuid_key),
90            "550e8400-e29b-41d4-a716-446655440000"
91        );
92
93        let mut fields = HashMap::new();
94        fields.insert("user_id".to_string(), "1".to_string());
95        fields.insert("role_id".to_string(), "2".to_string());
96        let composite_key = PrimaryKey::Composite(fields);
97        let display_str = format!("{}", composite_key);
98        assert!(display_str.contains("user_id:1") && display_str.contains("role_id:2"));
99    }
100
101    #[test]
102    fn test_primary_key_validation() {
103        assert!(!PrimaryKey::Integer(0).is_valid());
104        assert!(PrimaryKey::Integer(1).is_valid());
105
106        assert!(!PrimaryKey::Uuid(Uuid::nil()).is_valid());
107        assert!(PrimaryKey::Uuid(Uuid::new_v4()).is_valid());
108
109        assert!(!PrimaryKey::Composite(HashMap::new()).is_valid());
110        let mut fields = HashMap::new();
111        fields.insert("id".to_string(), "1".to_string());
112        assert!(PrimaryKey::Composite(fields).is_valid());
113    }
114}