things3_common/
constants.rs1pub const DATABASE_FILENAME: &str = "main.sqlite";
5
6pub const DATABASE_DIR: &str = "Things Database.thingsdatabase";
8
9pub const THINGS_CONTAINER: &str = "JLMPQHK8H4.com.culturedcode.Things3";
11
12pub const DEFAULT_QUERY_LIMIT: usize = 100;
14
15pub const MAX_QUERY_LIMIT: usize = 1000;
17
18pub const DEFAULT_MCP_PORT: u16 = 3000;
20
21pub const DATE_FORMATS: &[&str] = &["%Y-%m-%d", "%m/%d/%Y", "%d/%m/%Y"];
23
24pub const DATETIME_FORMATS: &[&str] = &[
26 "%Y-%m-%d %H:%M:%S",
27 "%Y-%m-%dT%H:%M:%S",
28 "%Y-%m-%d %H:%M:%S UTC",
29];
30
31#[cfg(test)]
32mod tests {
33 use super::*;
34
35 #[test]
36 fn test_database_filename() {
37 assert_eq!(DATABASE_FILENAME, "main.sqlite");
38 }
39
40 #[test]
41 fn test_database_dir() {
42 assert_eq!(DATABASE_DIR, "Things Database.thingsdatabase");
43 }
44
45 #[test]
46 fn test_things_container() {
47 assert_eq!(THINGS_CONTAINER, "JLMPQHK8H4.com.culturedcode.Things3");
48 }
49
50 #[test]
51 fn test_default_query_limit() {
52 assert_eq!(DEFAULT_QUERY_LIMIT, 100);
53 }
54
55 #[test]
56 fn test_max_query_limit() {
57 assert_eq!(MAX_QUERY_LIMIT, 1000);
58 }
59
60 #[test]
61 fn test_default_mcp_port() {
62 assert_eq!(DEFAULT_MCP_PORT, 3000);
63 }
64
65 #[test]
66 fn test_date_formats() {
67 assert_eq!(DATE_FORMATS.len(), 3);
68 assert!(DATE_FORMATS.contains(&"%Y-%m-%d"));
69 assert!(DATE_FORMATS.contains(&"%m/%d/%Y"));
70 assert!(DATE_FORMATS.contains(&"%d/%m/%Y"));
71 }
72
73 #[test]
74 fn test_datetime_formats() {
75 assert_eq!(DATETIME_FORMATS.len(), 3);
76 assert!(DATETIME_FORMATS.contains(&"%Y-%m-%d %H:%M:%S"));
77 assert!(DATETIME_FORMATS.contains(&"%Y-%m-%dT%H:%M:%S"));
78 assert!(DATETIME_FORMATS.contains(&"%Y-%m-%d %H:%M:%S UTC"));
79 }
80
81 #[test]
82 fn test_constants_are_public() {
83 let _ = DATABASE_FILENAME;
85 let _ = DATABASE_DIR;
86 let _ = THINGS_CONTAINER;
87 let _ = DEFAULT_QUERY_LIMIT;
88 let _ = MAX_QUERY_LIMIT;
89 let _ = DEFAULT_MCP_PORT;
90 let _ = DATE_FORMATS;
91 let _ = DATETIME_FORMATS;
92 }
93
94 #[test]
95 fn test_constants_types_and_values() {
96 assert!(DATABASE_FILENAME.ends_with(".sqlite"));
100 assert!(DATABASE_DIR.contains("Things"));
101 assert!(THINGS_CONTAINER.contains(".com."));
102
103 for format in DATE_FORMATS {
110 assert!(format.contains('%'));
111 assert!(format.contains('Y')); }
113
114 for format in DATETIME_FORMATS {
115 assert!(format.contains('%'));
116 assert!(format.contains('Y')); assert!(format.contains('H') || format.contains('M') || format.contains('S'));
118 }
120 }
121
122 #[test]
123 fn test_constants_immutability_and_static_nature() {
124 let db_filename_ref: &'static str = DATABASE_FILENAME;
128 let db_dir_ref: &'static str = DATABASE_DIR;
129 let container_ref: &'static str = THINGS_CONTAINER;
130
131 assert_eq!(db_filename_ref, "main.sqlite");
132 assert_eq!(db_dir_ref, "Things Database.thingsdatabase");
133 assert_eq!(container_ref, "JLMPQHK8H4.com.culturedcode.Things3");
134
135 let default_limit: usize = DEFAULT_QUERY_LIMIT;
137 let max_limit: usize = MAX_QUERY_LIMIT;
138 let port: u16 = DEFAULT_MCP_PORT;
139
140 assert_eq!(default_limit, 100);
141 assert_eq!(max_limit, 1000);
142 assert_eq!(port, 3000);
143
144 let date_formats_ref: &'static [&'static str] = DATE_FORMATS;
146 let datetime_formats_ref: &'static [&'static str] = DATETIME_FORMATS;
147
148 assert_eq!(date_formats_ref.len(), 3);
149 assert_eq!(datetime_formats_ref.len(), 3);
150 }
151
152 #[test]
153 fn test_constants_usage_scenarios() {
154 let db_path = format!("/{DATABASE_DIR}/{DATABASE_FILENAME}");
158 assert!(db_path.contains("Things Database.thingsdatabase"));
159 assert!(db_path.contains("main.sqlite"));
160
161 let container_path = format!("/Library/Group Containers/{THINGS_CONTAINER}");
163 assert!(container_path.contains("JLMPQHK8H4.com.culturedcode.Things3"));
164
165 let clamped_limit = std::cmp::min(500, MAX_QUERY_LIMIT);
167 assert_eq!(clamped_limit, 500);
168
169 let server_url = format!("http://localhost:{DEFAULT_MCP_PORT}");
171 assert_eq!(server_url, "http://localhost:3000");
172
173 assert!(DATE_FORMATS.contains(&"%Y-%m-%d"));
175 assert!(DATETIME_FORMATS.iter().any(|&f| f.contains("UTC")));
176 }
177
178 #[test]
179 fn test_constants_comprehensive_coverage() {
180 for (i, format) in DATE_FORMATS.iter().enumerate() {
184 assert!(format.contains('%'), "Date format {i} should contain %");
185 assert!(format.contains('Y'), "Date format {i} should contain Y");
186 assert!(!format.is_empty(), "Date format {i} should not be empty");
187 }
188
189 for (i, format) in DATETIME_FORMATS.iter().enumerate() {
191 assert!(format.contains('%'), "DateTime format {i} should contain %");
192 assert!(format.contains('Y'), "DateTime format {i} should contain Y");
193 assert!(
194 !format.is_empty(),
195 "DateTime format {i} should not be empty"
196 );
197 }
198
199 let db_filename_upper = DATABASE_FILENAME.to_uppercase();
201 assert!(db_filename_upper.contains("SQLITE"));
202
203 let db_dir_lower = DATABASE_DIR.to_lowercase();
204 assert!(db_dir_lower.contains("things"));
205
206 let container_parts: Vec<&str> = THINGS_CONTAINER.split('.').collect();
207 assert!(container_parts.len() >= 3);
208
209 let total_limit = DEFAULT_QUERY_LIMIT + MAX_QUERY_LIMIT;
211 assert!(total_limit > DEFAULT_QUERY_LIMIT);
212 assert!(total_limit > MAX_QUERY_LIMIT);
213
214 let port_range = u32::from(DEFAULT_MCP_PORT) + 1000;
215 assert!(port_range > u32::from(DEFAULT_MCP_PORT));
216
217 let date_format_count = DATE_FORMATS.len();
219 let datetime_format_count = DATETIME_FORMATS.len();
220 assert!(date_format_count > 0);
221 assert!(datetime_format_count > 0);
222
223 let first_date_format = DATE_FORMATS.first().unwrap();
225 let last_date_format = DATE_FORMATS.last().unwrap();
226 assert!(!first_date_format.is_empty());
227 assert!(!last_date_format.is_empty());
228
229 let first_datetime_format = DATETIME_FORMATS.first().unwrap();
230 let last_datetime_format = DATETIME_FORMATS.last().unwrap();
231 assert!(!first_datetime_format.is_empty());
232 assert!(!last_datetime_format.is_empty());
233 }
234
235 #[test]
236 fn test_constants_edge_cases_and_boundaries() {
237 assert!(!DATABASE_FILENAME.trim().is_empty());
241 assert!(!DATABASE_DIR.trim().is_empty());
242 assert!(!THINGS_CONTAINER.trim().is_empty());
243
244 let default_limit = DEFAULT_QUERY_LIMIT;
247 let max_limit = MAX_QUERY_LIMIT;
248 assert!(default_limit > 0);
249 assert!(default_limit <= max_limit);
250 assert!(max_limit > default_limit);
251
252 let port_value = DEFAULT_MCP_PORT;
254 assert!(port_value > 1024); assert!(port_value < 65535); for format in DATE_FORMATS {
259 let percent_count = format.chars().filter(|&c| c == '%').count();
261 assert!(
262 percent_count > 0,
263 "Format should have at least one % specifier: {format}"
264 );
265 }
266
267 for format in DATETIME_FORMATS {
268 let percent_count = format.chars().filter(|&c| c == '%').count();
270 assert!(
271 percent_count > 0,
272 "Format should have at least one % specifier: {format}"
273 );
274 }
275
276 assert!(DATABASE_FILENAME.ends_with(".sqlite"));
278 assert!(DATABASE_DIR.contains("Database"));
279 assert!(THINGS_CONTAINER.contains("com.culturedcode"));
280
281 let _as_bytes = DATABASE_FILENAME.as_bytes();
283 let _as_chars: Vec<char> = DATABASE_DIR.chars().collect();
284 let _as_string = THINGS_CONTAINER.to_string();
285
286 let mut date_format_iter = DATE_FORMATS.iter();
288 assert!(date_format_iter.next().is_some());
289
290 let mut datetime_format_iter = DATETIME_FORMATS.iter();
291 assert!(datetime_format_iter.next().is_some());
292 }
293
294 #[test]
295 fn test_constants_comprehensive_string_operations() {
296 assert_eq!(DATABASE_FILENAME.len(), 11); assert!(DATABASE_FILENAME.starts_with("main"));
301 assert!(DATABASE_FILENAME.ends_with(".sqlite"));
302 assert!(DATABASE_FILENAME.contains('.'));
303 assert!(DATABASE_FILENAME.is_ascii());
305
306 let filename_bytes = DATABASE_FILENAME.as_bytes();
308 assert_eq!(filename_bytes[0], b'm');
309 assert_eq!(filename_bytes[filename_bytes.len() - 1], b'e');
310
311 assert!(DATABASE_DIR.len() > 10);
313 assert!(DATABASE_DIR.starts_with("Things"));
314 assert!(DATABASE_DIR.ends_with(".thingsdatabase"));
315 assert!(DATABASE_DIR.contains("Database"));
316 assert!(DATABASE_DIR.contains(' '));
317 assert!(DATABASE_DIR.is_ascii());
319
320 assert!(THINGS_CONTAINER.len() > 20);
322 assert!(THINGS_CONTAINER.starts_with("JLMPQHK8H4"));
323 assert!(THINGS_CONTAINER.ends_with("Things3"));
324 assert!(THINGS_CONTAINER.contains(".com."));
325 assert!(THINGS_CONTAINER.contains("culturedcode"));
326 assert!(THINGS_CONTAINER.is_ascii());
328
329 let upper_filename = DATABASE_FILENAME.to_uppercase();
331 assert_eq!(upper_filename, "MAIN.SQLITE");
332
333 let lower_dir = DATABASE_DIR.to_lowercase();
334 assert!(lower_dir.contains("things"));
335 assert!(lower_dir.contains("database"));
336
337 let container_parts: Vec<&str> = THINGS_CONTAINER.split('.').collect();
339 assert!(container_parts.len() >= 4);
340 assert_eq!(container_parts[0], "JLMPQHK8H4");
341 assert_eq!(container_parts[1], "com");
342 assert_eq!(container_parts[2], "culturedcode");
343 assert_eq!(container_parts[3], "Things3");
344
345 let modified_filename = DATABASE_FILENAME.replace("main", "test");
347 assert_eq!(modified_filename, "test.sqlite");
348
349 let modified_dir = DATABASE_DIR.replace("Things", "Test");
350 assert!(modified_dir.contains("Test Database"));
351 }
352
353 #[test]
354 fn test_constants_array_comprehensive_operations() {
355 assert_eq!(DATE_FORMATS.len(), 3);
359 assert_eq!(DATE_FORMATS[0], "%Y-%m-%d");
363 assert_eq!(DATE_FORMATS[1], "%m/%d/%Y");
364 assert_eq!(DATE_FORMATS[2], "%d/%m/%Y");
365
366 assert!(DATE_FORMATS.contains(&"%Y-%m-%d"));
368 assert!(DATE_FORMATS.contains(&"%m/%d/%Y"));
369 assert!(DATE_FORMATS.contains(&"%d/%m/%Y"));
370 assert!(!DATE_FORMATS.contains(&"%Y/%m/%d"));
371
372 assert_eq!(DATE_FORMATS.first(), Some(&"%Y-%m-%d"));
374 assert_eq!(DATE_FORMATS.last(), Some(&"%d/%m/%Y"));
375
376 let mut count = 0;
378 for format in DATE_FORMATS {
379 assert!(format.contains('%'));
380 assert!(format.contains('Y'));
381 count += 1;
382 }
383 assert_eq!(count, 3);
384
385 assert_eq!(DATETIME_FORMATS.len(), 3);
387 assert_eq!(DATETIME_FORMATS[0], "%Y-%m-%d %H:%M:%S");
391 assert_eq!(DATETIME_FORMATS[1], "%Y-%m-%dT%H:%M:%S");
392 assert_eq!(DATETIME_FORMATS[2], "%Y-%m-%d %H:%M:%S UTC");
393
394 assert!(DATETIME_FORMATS.contains(&"%Y-%m-%d %H:%M:%S"));
396 assert!(DATETIME_FORMATS.contains(&"%Y-%m-%dT%H:%M:%S"));
397 assert!(DATETIME_FORMATS.contains(&"%Y-%m-%d %H:%M:%S UTC"));
398 assert!(!DATETIME_FORMATS.contains(&"%Y/%m/%d %H:%M:%S"));
399
400 assert_eq!(DATETIME_FORMATS.first(), Some(&"%Y-%m-%d %H:%M:%S"));
402 assert_eq!(DATETIME_FORMATS.last(), Some(&"%Y-%m-%d %H:%M:%S UTC"));
403
404 let mut datetime_count = 0;
406 for format in DATETIME_FORMATS {
407 assert!(format.contains('%'));
408 assert!(format.contains('Y'));
409 assert!(format.contains('H') || format.contains('M') || format.contains('S'));
410 datetime_count += 1;
411 }
412 assert_eq!(datetime_count, 3);
413
414 let date_slice = &DATE_FORMATS[0..2];
416 assert_eq!(date_slice.len(), 2);
417 assert_eq!(date_slice[0], "%Y-%m-%d");
418 assert_eq!(date_slice[1], "%m/%d/%Y");
419
420 let datetime_slice = &DATETIME_FORMATS[1..];
421 assert_eq!(datetime_slice.len(), 2);
422 assert_eq!(datetime_slice[0], "%Y-%m-%dT%H:%M:%S");
423 assert_eq!(datetime_slice[1], "%Y-%m-%d %H:%M:%S UTC");
424 }
425
426 #[test]
427 fn test_constants_numeric_comprehensive_operations() {
428 assert_eq!(DEFAULT_QUERY_LIMIT, 100);
432 let doubled_default = DEFAULT_QUERY_LIMIT * 2;
436 assert_eq!(doubled_default, 200);
437
438 let halved_default = DEFAULT_QUERY_LIMIT / 2;
439 assert_eq!(halved_default, 50);
440
441 let sum_limits = DEFAULT_QUERY_LIMIT + MAX_QUERY_LIMIT;
442 assert_eq!(sum_limits, 1100);
443
444 assert_eq!(MAX_QUERY_LIMIT, 1000);
446 let default_limit = DEFAULT_QUERY_LIMIT;
450 let max_limit = MAX_QUERY_LIMIT;
451 assert!(default_limit < max_limit);
452 assert!(max_limit > default_limit);
453 assert!(default_limit != max_limit);
454 assert_eq!(max_limit / default_limit, 10);
455
456 assert_eq!(DEFAULT_MCP_PORT, 3000);
458 let port_plus_one = DEFAULT_MCP_PORT + 1;
462 assert_eq!(port_plus_one, 3001);
463
464 let port_range = DEFAULT_MCP_PORT..DEFAULT_MCP_PORT + 10;
465 assert!(port_range.contains(&3005));
466 assert!(!port_range.contains(&3010));
467
468 let port_as_u32 = u32::from(DEFAULT_MCP_PORT);
470 assert_eq!(port_as_u32, 3000);
471
472 let port_as_i32_value = i32::from(DEFAULT_MCP_PORT);
473 assert_eq!(port_as_i32_value, 3000);
474
475 let default_limit: usize = DEFAULT_QUERY_LIMIT;
477 let max_limit: usize = MAX_QUERY_LIMIT;
478 let port_value: u16 = DEFAULT_MCP_PORT;
479
480 assert_eq!(default_limit, 100);
482 assert_eq!(max_limit, 1000);
483 assert_eq!(port_value, 3000);
484 }
485}