1pub struct CacheKey;
8
9impl CacheKey {
10 #[must_use]
12 pub fn search(query: &str, page: u32, per_page: u32) -> String {
13 format!(
14 "search:{}:page{}:per{}",
15 Self::hash_query(query),
16 page,
17 per_page
18 )
19 }
20
21 #[must_use]
23 pub fn pr_details(pr_api_url: &str) -> String {
24 format!("pr:details:{pr_api_url}")
25 }
26
27 #[must_use]
29 pub fn pr_reviews(pr_api_url: &str, page: u32) -> String {
30 format!("pr:reviews:{pr_api_url}:page{page}")
31 }
32
33 #[must_use]
35 pub fn mr_notes(project_id: u64, mr_iid: u64, page: u32) -> String {
36 format!("gitlab:mr:notes:project{project_id}:mr{mr_iid}:page{page}")
37 }
38
39 fn hash_query(query: &str) -> String {
40 use std::collections::hash_map::DefaultHasher;
41 use std::hash::{Hash, Hasher};
42
43 let mut hasher = DefaultHasher::new();
44 query.hash(&mut hasher);
45 format!("{:x}", hasher.finish())
46 }
47}
48
49#[cfg(test)]
50mod tests {
51 use super::CacheKey;
52
53 #[test]
54 fn search_key_has_expected_shape() {
55 let key = CacheKey::search("is:pr author:octocat", 3, 100);
56 assert!(key.starts_with("search:"));
57 assert!(key.ends_with(":page3:per100"));
58 }
59
60 #[test]
61 fn search_key_is_stable_for_same_input() {
62 let k1 = CacheKey::search("is:pr author:octocat", 1, 100);
63 let k2 = CacheKey::search("is:pr author:octocat", 1, 100);
64 assert_eq!(k1, k2);
65 }
66
67 #[test]
68 fn pr_details_key_has_expected_prefix() {
69 let key = CacheKey::pr_details("https://api.github.com/repos/o/r/pulls/1");
70 assert_eq!(key, "pr:details:https://api.github.com/repos/o/r/pulls/1");
71 }
72
73 #[test]
74 fn pr_reviews_key_has_expected_prefix_and_page() {
75 let key = CacheKey::pr_reviews("https://api.github.com/repos/o/r/pulls/1", 4);
76 assert_eq!(
77 key,
78 "pr:reviews:https://api.github.com/repos/o/r/pulls/1:page4"
79 );
80 }
81
82 #[test]
83 fn mr_notes_key_has_expected_segments() {
84 let key = CacheKey::mr_notes(42, 7, 2);
85 assert_eq!(key, "gitlab:mr:notes:project42:mr7:page2");
86 }
87}