1use std::sync::Arc;
2
3use shardline_protocol::{RepositoryProvider, RepositoryScope};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub struct RepositoryScopeCacheKey {
8 provider: &'static str,
9 owner: Arc<str>,
10 repo: Arc<str>,
11 revision: Option<Arc<str>>,
12}
13
14impl RepositoryScopeCacheKey {
15 #[must_use]
17 pub fn from_scope(scope: &RepositoryScope) -> Self {
18 Self {
19 provider: provider_token(scope.provider()),
20 owner: Arc::from(scope.owner()),
21 repo: Arc::from(scope.name()),
22 revision: scope.revision().map(Arc::from),
23 }
24 }
25
26 #[must_use]
28 pub fn provider(&self) -> &str {
29 self.provider
30 }
31
32 #[must_use]
34 pub fn owner(&self) -> &str {
35 &self.owner
36 }
37
38 #[must_use]
40 pub fn repo(&self) -> &str {
41 &self.repo
42 }
43
44 #[must_use]
46 pub fn revision(&self) -> Option<&str> {
47 self.revision.as_deref()
48 }
49}
50
51#[derive(Debug, Clone, PartialEq, Eq, Hash)]
53pub struct ReconstructionCacheKey {
54 file_id: String,
55 content_hash: Option<String>,
56 repository_scope: Option<RepositoryScopeCacheKey>,
57}
58
59impl ReconstructionCacheKey {
60 #[must_use]
62 pub fn latest(file_id: &str, repository_scope: Option<&RepositoryScope>) -> Self {
63 Self {
64 file_id: file_id.to_owned(),
65 content_hash: None,
66 repository_scope: repository_scope.map(RepositoryScopeCacheKey::from_scope),
67 }
68 }
69
70 #[must_use]
72 pub fn version(
73 file_id: &str,
74 content_hash: &str,
75 repository_scope: Option<&RepositoryScope>,
76 ) -> Self {
77 Self {
78 file_id: file_id.to_owned(),
79 content_hash: Some(content_hash.to_owned()),
80 repository_scope: repository_scope.map(RepositoryScopeCacheKey::from_scope),
81 }
82 }
83
84 #[must_use]
86 pub fn file_id(&self) -> &str {
87 &self.file_id
88 }
89
90 #[must_use]
92 pub fn content_hash(&self) -> Option<&str> {
93 self.content_hash.as_deref()
94 }
95
96 #[must_use]
98 pub const fn repository_scope(&self) -> Option<&RepositoryScopeCacheKey> {
99 self.repository_scope.as_ref()
100 }
101}
102
103const fn provider_token(provider: RepositoryProvider) -> &'static str {
104 match provider {
105 RepositoryProvider::GitHub => "github",
106 RepositoryProvider::Gitea => "gitea",
107 RepositoryProvider::GitLab => "gitlab",
108 RepositoryProvider::Codeberg => "codeberg",
109 RepositoryProvider::Generic => "generic",
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use shardline_protocol::{RepositoryProvider, RepositoryScope};
116
117 use super::ReconstructionCacheKey;
118
119 #[test]
120 fn cache_key_keeps_version_and_scope_components() {
121 let scope =
122 RepositoryScope::new(RepositoryProvider::GitHub, "team", "assets", Some("main"));
123 assert!(scope.is_ok());
124 let Ok(scope) = scope else {
125 return;
126 };
127
128 let latest = ReconstructionCacheKey::latest("asset.bin", Some(&scope));
129 let version = ReconstructionCacheKey::version("asset.bin", "deadbeef", Some(&scope));
130
131 assert_eq!(latest.file_id(), "asset.bin");
132 assert_eq!(latest.content_hash(), None);
133 assert_eq!(version.content_hash(), Some("deadbeef"));
134 let latest_scope = latest.repository_scope();
135 assert!(latest_scope.is_some());
136 let Some(latest_scope) = latest_scope else {
137 return;
138 };
139 assert_eq!(latest_scope.owner(), "team");
140 assert_eq!(latest_scope.repo(), "assets");
141 assert_eq!(latest_scope.provider(), "github");
142 assert_eq!(latest_scope.revision(), Some("main"));
143 }
144}