oxify_storage/
db_utils.rs

1//! Database utility functions
2//!
3//! Common database operations and type conversions used across store modules.
4
5use uuid::Uuid;
6
7/// Parse a vector of UUID strings to UUIDs, filtering out invalid ones
8///
9/// This is useful when retrieving UUID arrays from PostgreSQL text[] columns.
10///
11/// # Examples
12/// ```
13/// # use oxify_storage::db_utils::parse_uuid_array;
14/// # use uuid::Uuid;
15/// let valid_uuid = Uuid::new_v4().to_string();
16/// let input = vec![
17///     valid_uuid.clone(),
18///     "invalid-uuid".to_string(),
19///     Uuid::new_v4().to_string(),
20/// ];
21/// let result = parse_uuid_array(input);
22/// assert_eq!(result.len(), 2); // Only valid UUIDs
23/// ```
24pub fn parse_uuid_array(strings: Vec<String>) -> Vec<Uuid> {
25    strings
26        .iter()
27        .filter_map(|s| Uuid::parse_str(s).ok())
28        .collect()
29}
30
31/// Convert `Vec<Uuid>` to `Vec<String>` for database storage
32///
33/// # Examples
34/// ```
35/// # use oxify_storage::db_utils::uuids_to_strings;
36/// # use uuid::Uuid;
37/// let uuids = vec![Uuid::new_v4(), Uuid::new_v4()];
38/// let strings = uuids_to_strings(&uuids);
39/// assert_eq!(strings.len(), 2);
40/// ```
41pub fn uuids_to_strings(uuids: &[Uuid]) -> Vec<String> {
42    uuids.iter().map(|id| id.to_string()).collect()
43}
44
45/// Safe conversion from `i64` to `u64` with validation
46///
47/// Negative values are clamped to 0.
48///
49/// # Examples
50/// ```
51/// # use oxify_storage::db_utils::i64_to_u64_safe;
52/// assert_eq!(i64_to_u64_safe(100), 100);
53/// assert_eq!(i64_to_u64_safe(0), 0);
54/// assert_eq!(i64_to_u64_safe(-1), 0);
55/// assert_eq!(i64_to_u64_safe(-999), 0);
56/// ```
57#[inline]
58pub fn i64_to_u64_safe(value: i64) -> u64 {
59    value.max(0) as u64
60}
61
62/// Safe conversion from `Option<i64>` to `Option<u64>`
63///
64/// Negative values are clamped to 0.
65///
66/// # Examples
67/// ```
68/// # use oxify_storage::db_utils::opt_i64_to_u64;
69/// assert_eq!(opt_i64_to_u64(Some(100)), Some(100));
70/// assert_eq!(opt_i64_to_u64(Some(-1)), Some(0));
71/// assert_eq!(opt_i64_to_u64(None), None);
72/// ```
73#[inline]
74pub fn opt_i64_to_u64(value: Option<i64>) -> Option<u64> {
75    value.map(|v| v.max(0) as u64)
76}
77
78/// Convert boolean to integer for database storage (PostgreSQL compatible)
79///
80/// # Examples
81/// ```
82/// # use oxify_storage::db_utils::bool_to_int;
83/// assert_eq!(bool_to_int(true), 1);
84/// assert_eq!(bool_to_int(false), 0);
85/// ```
86#[inline]
87pub fn bool_to_int(value: bool) -> i32 {
88    if value {
89        1
90    } else {
91        0
92    }
93}
94
95/// Convert integer to boolean from database
96///
97/// # Examples
98/// ```
99/// # use oxify_storage::db_utils::int_to_bool;
100/// assert_eq!(int_to_bool(1), true);
101/// assert_eq!(int_to_bool(0), false);
102/// assert_eq!(int_to_bool(42), true); // Any non-zero is true
103/// ```
104#[inline]
105pub fn int_to_bool(value: i32) -> bool {
106    value != 0
107}
108
109#[cfg(test)]
110mod tests {
111    use super::*;
112
113    #[test]
114    fn test_parse_uuid_array() {
115        let uuid1 = Uuid::new_v4();
116        let uuid2 = Uuid::new_v4();
117
118        let input = vec![
119            uuid1.to_string(),
120            "invalid-uuid".to_string(),
121            uuid2.to_string(),
122            "not-a-uuid-at-all".to_string(),
123        ];
124
125        let result = parse_uuid_array(input);
126        assert_eq!(result.len(), 2);
127        assert!(result.contains(&uuid1));
128        assert!(result.contains(&uuid2));
129    }
130
131    #[test]
132    fn test_parse_uuid_array_all_invalid() {
133        let input = vec!["invalid-uuid".to_string(), "not-a-uuid".to_string()];
134
135        let result = parse_uuid_array(input);
136        assert_eq!(result.len(), 0);
137    }
138
139    #[test]
140    fn test_parse_uuid_array_empty() {
141        let input: Vec<String> = vec![];
142        let result = parse_uuid_array(input);
143        assert_eq!(result.len(), 0);
144    }
145
146    #[test]
147    fn test_uuids_to_strings() {
148        let uuid1 = Uuid::new_v4();
149        let uuid2 = Uuid::new_v4();
150        let uuids = vec![uuid1, uuid2];
151
152        let strings = uuids_to_strings(&uuids);
153        assert_eq!(strings.len(), 2);
154        assert_eq!(strings[0], uuid1.to_string());
155        assert_eq!(strings[1], uuid2.to_string());
156    }
157
158    #[test]
159    fn test_uuids_to_strings_empty() {
160        let uuids: Vec<Uuid> = vec![];
161        let strings = uuids_to_strings(&uuids);
162        assert_eq!(strings.len(), 0);
163    }
164
165    #[test]
166    fn test_i64_to_u64_safe() {
167        assert_eq!(i64_to_u64_safe(100), 100);
168        assert_eq!(i64_to_u64_safe(0), 0);
169        assert_eq!(i64_to_u64_safe(-1), 0);
170        assert_eq!(i64_to_u64_safe(-999), 0);
171        assert_eq!(i64_to_u64_safe(i64::MAX), i64::MAX as u64);
172    }
173
174    #[test]
175    fn test_opt_i64_to_u64() {
176        assert_eq!(opt_i64_to_u64(Some(100)), Some(100));
177        assert_eq!(opt_i64_to_u64(Some(0)), Some(0));
178        assert_eq!(opt_i64_to_u64(Some(-1)), Some(0));
179        assert_eq!(opt_i64_to_u64(Some(-999)), Some(0));
180        assert_eq!(opt_i64_to_u64(None), None);
181    }
182
183    #[test]
184    fn test_bool_to_int() {
185        assert_eq!(bool_to_int(true), 1);
186        assert_eq!(bool_to_int(false), 0);
187    }
188
189    #[test]
190    fn test_int_to_bool() {
191        assert!(int_to_bool(1));
192        assert!(!int_to_bool(0));
193        assert!(int_to_bool(42));
194        assert!(int_to_bool(-1));
195    }
196
197    #[test]
198    fn test_roundtrip_bool_int() {
199        assert!(int_to_bool(bool_to_int(true)));
200        assert!(!int_to_bool(bool_to_int(false)));
201    }
202}