This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / crates / knot-index / src / lib.rs
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}