This repository has no description
7.7 kB
240 lines
1mod coverage;
2mod error;
3mod intern;
4mod projections;
5
6pub use coverage::{Coverage, Resolved};
7pub use error::IndexError;
8pub use knot_types::OfferedKey;
9
10use std::path::PathBuf;
11use std::sync::atomic::{AtomicU64, Ordering};
12
13use knot_cob::{ChangePayload, CobStore};
14use knot_cobs::{
15 BlocklistChange, BlocklistCob, CollaboratorsChange, CollaboratorsCob, Grant, MembersChange,
16 MembersCob, RegistryChange, RepoRegistryCob,
17};
18use knot_git::{Layout, Repo};
19use knot_types::{AccountDid, ClonePath, OwnerDid, RepoDid, RepoRkey};
20
21use intern::Interner;
22use projections::{CollaboratorsProjection, GrantSetProjection, KeyProjection, RegistryProjection};
23
24knot_types::scalar_newtype! {
25 pub struct IndexGeneration(u64);
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct IndexCoverage {
30 pub members: Coverage,
31 pub blocklist: Coverage,
32 pub collaborators: Coverage,
33 pub registry: Coverage,
34 pub keys: Coverage,
35}
36
37pub struct Index {
38 meta_path: PathBuf,
39 layout: Layout,
40 interner: Interner,
41 members: GrantSetProjection<MembersCob>,
42 blocklist: GrantSetProjection<BlocklistCob>,
43 collaborators: CollaboratorsProjection,
44 registry: RegistryProjection,
45 keys: KeyProjection,
46 generation: AtomicU64,
47}
48
49impl Index {
50 pub fn new(meta_path: impl Into<PathBuf>, layout: Layout) -> Self {
51 Self {
52 meta_path: meta_path.into(),
53 layout,
54 interner: Interner::new(),
55 members: GrantSetProjection::new(),
56 blocklist: GrantSetProjection::new(),
57 collaborators: CollaboratorsProjection::new(),
58 registry: RegistryProjection::new(),
59 keys: KeyProjection::new(),
60 generation: AtomicU64::new(0),
61 }
62 }
63
64 pub fn generation(&self) -> IndexGeneration {
65 IndexGeneration(self.generation.load(Ordering::Acquire))
66 }
67
68 fn bump_generation(&self) {
69 self.generation.fetch_add(1, Ordering::Release);
70 }
71
72 pub fn rebuild(&self) -> Result<(), IndexError> {
73 self.refresh_members()?;
74 self.refresh_blocklist()?;
75 self.refresh_registry()?;
76 Ok(())
77 }
78
79 pub fn warm_collaborators(&self) {
80 self.hosted_repos().iter().for_each(|repo| {
81 let _ = self.ensure_collaborators(repo);
82 });
83 }
84
85 pub fn refresh_members(&self) -> Result<(), IndexError> {
86 let meta = Repo::open(&self.meta_path)?;
87 let store = CobStore::new(&meta);
88 match store.list::<MembersCob>()?.as_slice() {
89 [] => self.members.reset(),
90 [object] => self.members.refresh(&self.interner, &store, *object)?,
91 many => {
92 return Err(IndexError::Ambiguous {
93 type_name: MembersChange::type_name(),
94 count: many.len(),
95 });
96 }
97 }
98 self.bump_generation();
99 Ok(())
100 }
101
102 pub fn refresh_blocklist(&self) -> Result<(), IndexError> {
103 let meta = Repo::open(&self.meta_path)?;
104 let store = CobStore::new(&meta);
105 match store.list::<BlocklistCob>()?.as_slice() {
106 [] => self.blocklist.reset(),
107 [object] => self.blocklist.refresh(&self.interner, &store, *object)?,
108 many => {
109 return Err(IndexError::Ambiguous {
110 type_name: BlocklistChange::type_name(),
111 count: many.len(),
112 });
113 }
114 }
115 self.bump_generation();
116 Ok(())
117 }
118
119 pub fn refresh_registry(&self) -> Result<(), IndexError> {
120 let meta = Repo::open(&self.meta_path)?;
121 let store = CobStore::new(&meta);
122 let evacuated = match store.list::<RepoRegistryCob>()?.as_slice() {
123 [] => self.registry.reset(&self.interner),
124 [object] => self.registry.refresh(&self.interner, &store, *object)?,
125 many => {
126 return Err(IndexError::Ambiguous {
127 type_name: RegistryChange::type_name(),
128 count: many.len(),
129 });
130 }
131 };
132 evacuated.iter().for_each(|repo| {
133 if let Some(key) = self.interner.repo(repo) {
134 self.collaborators.drop_repo(key);
135 }
136 });
137 self.bump_generation();
138 Ok(())
139 }
140
141 pub fn ensure_collaborators(&self, repo: &RepoDid) -> Result<(), IndexError> {
142 if self.collaborators.is_folded(&self.interner, repo) {
143 return Ok(());
144 }
145 self.refresh_collaborators(repo)
146 }
147
148 pub fn refresh_collaborators(&self, repo: &RepoDid) -> Result<(), IndexError> {
149 let git = self.layout.open(repo)?;
150 let store = CobStore::new(&git);
151 let repo_key = self.interner.intern_repo(repo);
152 match store.list::<CollaboratorsCob>()?.as_slice() {
153 [] => self.collaborators.mark_repo_empty(repo_key),
154 [object] => {
155 self.collaborators
156 .refresh_repo(&self.interner, &store, repo_key, *object)?
157 }
158 many => {
159 return Err(IndexError::Ambiguous {
160 type_name: CollaboratorsChange::type_name(),
161 count: many.len(),
162 });
163 }
164 }
165 self.bump_generation();
166 Ok(())
167 }
168
169 pub fn is_member(&self, did: &AccountDid) -> Resolved<bool> {
170 self.members.contains(&self.interner, did)
171 }
172
173 pub fn member_entries(&self) -> Resolved<Vec<Grant>> {
174 self.members.entries(&self.interner)
175 }
176
177 pub fn is_blocked(&self, did: &AccountDid) -> Resolved<bool> {
178 self.blocklist.contains(&self.interner, did)
179 }
180
181 pub fn blocked_entries(&self) -> Resolved<Vec<Grant>> {
182 self.blocklist.entries(&self.interner)
183 }
184
185 pub fn is_collaborator(&self, repo: &RepoDid, did: &AccountDid) -> Resolved<bool> {
186 self.collaborators.contains(&self.interner, repo, did)
187 }
188
189 pub fn collaborator_entries(&self, repo: &RepoDid) -> Resolved<Vec<Grant>> {
190 self.collaborators.entries(&self.interner, repo)
191 }
192
193 pub fn collaborators_of(&self, repo: &RepoDid) -> Resolved<Vec<AccountDid>> {
194 self.collaborator_entries(repo)
195 .map(|entries| entries.into_iter().map(|grant| grant.subject).collect())
196 }
197
198 pub fn resolve_repo(&self, owner: &OwnerDid, rkey: &RepoRkey) -> Resolved<Option<RepoDid>> {
199 self.registry.resolve(&self.interner, owner, rkey)
200 }
201
202 pub fn resolve_clone_path(
203 &self,
204 owner: &OwnerDid,
205 path: &ClonePath,
206 ) -> Resolved<Option<RepoDid>> {
207 self.registry
208 .resolve_clone_path(&self.interner, owner, path)
209 }
210
211 pub fn owner_of(&self, repo: &RepoDid) -> Resolved<Option<OwnerDid>> {
212 self.registry.owner_of(&self.interner, repo)
213 }
214
215 pub fn rkey_of(&self, repo: &RepoDid) -> Resolved<Option<RepoRkey>> {
216 self.registry.rkey_of(&self.interner, repo)
217 }
218
219 pub fn hosted_repos(&self) -> Vec<RepoDid> {
220 self.registry.hosted_repos(&self.interner)
221 }
222
223 pub fn owner_of_key(&self, key: &OfferedKey) -> Resolved<Option<AccountDid>> {
224 self.keys.owner(&self.interner, key)
225 }
226
227 pub fn cache_key(&self, key: OfferedKey, did: &AccountDid) {
228 self.keys.cache(&self.interner, key, did);
229 }
230
231 pub fn coverage(&self) -> IndexCoverage {
232 IndexCoverage {
233 members: self.members.coverage(),
234 blocklist: self.blocklist.coverage(),
235 collaborators: self.collaborators.coverage(),
236 registry: self.registry.coverage(),
237 keys: self.keys.coverage(),
238 }
239 }
240}