lore_cli/capture/watchers/
common.rs1use chrono::{DateTime, TimeZone, Utc};
8use std::path::PathBuf;
9use uuid::Uuid;
10
11use crate::storage::models::MessageRole;
12
13pub fn vscode_global_storage() -> PathBuf {
20 #[cfg(target_os = "macos")]
21 {
22 dirs::home_dir()
23 .unwrap_or_else(|| PathBuf::from("."))
24 .join("Library/Application Support/Code/User/globalStorage")
25 }
26 #[cfg(target_os = "linux")]
27 {
28 dirs::config_dir()
29 .unwrap_or_else(|| PathBuf::from("."))
30 .join("Code/User/globalStorage")
31 }
32 #[cfg(target_os = "windows")]
33 {
34 dirs::config_dir()
35 .unwrap_or_else(|| PathBuf::from("."))
36 .join("Code/User/globalStorage")
37 }
38 #[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "windows")))]
39 {
40 dirs::config_dir()
41 .unwrap_or_else(|| PathBuf::from("."))
42 .join("Code/User/globalStorage")
43 }
44}
45
46pub fn parse_role(role: &str) -> Option<MessageRole> {
55 match role {
56 "user" | "human" => Some(MessageRole::User),
57 "assistant" => Some(MessageRole::Assistant),
58 "system" => Some(MessageRole::System),
59 _ => None,
60 }
61}
62
63pub fn parse_timestamp_millis(ms: i64) -> Option<DateTime<Utc>> {
67 Utc.timestamp_millis_opt(ms).single()
68}
69
70pub fn parse_timestamp_rfc3339(s: &str) -> Option<DateTime<Utc>> {
74 DateTime::parse_from_rfc3339(s)
75 .ok()
76 .map(|dt| dt.with_timezone(&Utc))
77}
78
79#[allow(dead_code)]
84pub fn parse_uuid_or_generate(s: &str) -> Uuid {
85 Uuid::parse_str(s).unwrap_or_else(|_| Uuid::new_v4())
86}
87
88#[cfg(test)]
89mod tests {
90 use super::*;
91
92 #[test]
93 fn test_vscode_global_storage_returns_valid_path() {
94 let path = vscode_global_storage();
95 assert!(path.to_string_lossy().contains("globalStorage"));
97 }
98
99 #[test]
100 fn test_parse_role_user() {
101 assert_eq!(parse_role("user"), Some(MessageRole::User));
102 }
103
104 #[test]
105 fn test_parse_role_human() {
106 assert_eq!(parse_role("human"), Some(MessageRole::User));
107 }
108
109 #[test]
110 fn test_parse_role_assistant() {
111 assert_eq!(parse_role("assistant"), Some(MessageRole::Assistant));
112 }
113
114 #[test]
115 fn test_parse_role_system() {
116 assert_eq!(parse_role("system"), Some(MessageRole::System));
117 }
118
119 #[test]
120 fn test_parse_role_unknown() {
121 assert_eq!(parse_role("unknown"), None);
122 assert_eq!(parse_role(""), None);
123 assert_eq!(parse_role("thinking"), None);
124 assert_eq!(parse_role("tool"), None);
125 }
126
127 #[test]
128 fn test_parse_timestamp_millis_valid() {
129 let ts = parse_timestamp_millis(1704067200000);
130 assert!(ts.is_some());
131 let dt = ts.unwrap();
132 assert_eq!(dt.timestamp_millis(), 1704067200000);
133 }
134
135 #[test]
136 fn test_parse_timestamp_millis_zero() {
137 let ts = parse_timestamp_millis(0);
138 assert!(ts.is_some());
139 assert_eq!(ts.unwrap().timestamp(), 0);
140 }
141
142 #[test]
143 fn test_parse_timestamp_rfc3339_valid() {
144 let ts = parse_timestamp_rfc3339("2025-01-15T10:00:00.000Z");
145 assert!(ts.is_some());
146 let dt = ts.unwrap();
147 assert!(dt.to_rfc3339().contains("2025-01-15"));
148 }
149
150 #[test]
151 fn test_parse_timestamp_rfc3339_with_offset() {
152 let ts = parse_timestamp_rfc3339("2025-01-15T10:00:00-05:00");
153 assert!(ts.is_some());
154 }
155
156 #[test]
157 fn test_parse_timestamp_rfc3339_invalid() {
158 assert!(parse_timestamp_rfc3339("not a timestamp").is_none());
159 assert!(parse_timestamp_rfc3339("").is_none());
160 assert!(parse_timestamp_rfc3339("2025-01-15").is_none());
161 }
162
163 #[test]
164 fn test_parse_uuid_or_generate_valid_uuid() {
165 let uuid_str = "550e8400-e29b-41d4-a716-446655440000";
166 let uuid = parse_uuid_or_generate(uuid_str);
167 assert_eq!(uuid.to_string(), uuid_str);
168 }
169
170 #[test]
171 fn test_parse_uuid_or_generate_invalid_generates_new() {
172 let uuid = parse_uuid_or_generate("not-a-uuid");
173 assert!(!uuid.is_nil());
174 assert_eq!(uuid.get_version_num(), 4);
176 }
177
178 #[test]
179 fn test_parse_uuid_or_generate_empty_generates_new() {
180 let uuid = parse_uuid_or_generate("");
181 assert!(!uuid.is_nil());
182 }
183}