use std::collections::HashMap; use std::convert::Infallible; use std::io; use std::path::{Path, PathBuf}; use std::str::FromStr; use crypto::PublicKey; pub use crate::git; use crate::git::fmt; use crate::crypto::Verified; use crate::identity::doc::{Doc, DocAt, DocError, RawDoc, RepoId}; use crate::node::device::Device; use crate::node::NodeId; pub use crate::storage::*; use super::{arbitrary, fixtures}; #[derive(Clone, Debug)] pub struct MockStorage { pub path: PathBuf, pub info: crate::git::UserInfo, /// All refs keyed by RID. /// Each value is a map of refs keyed by node Id (public key). pub repos: HashMap, } impl MockStorage { pub fn new(inventory: Vec<(RepoId, DocAt)>) -> Self { Self { path: PathBuf::default(), info: fixtures::user(), repos: inventory .into_iter() .map(|(id, doc)| { ( id, MockRepository { id, doc, remotes: HashMap::new(), }, ) }) .collect(), } } pub fn repo_mut(&mut self, rid: &RepoId) -> &mut MockRepository { self.repos .get_mut(rid) .expect("MockStorage::repo_mut: repository does not exist") } pub fn map(mut self, f: impl Fn(&mut RawDoc)) -> Self { for repo in self.repos.values_mut() { repo.doc.doc = repo.doc.doc.clone().with_edits(|doc| f(doc)).unwrap(); } self } pub fn empty() -> Self { Self::new(Vec::new()) } } impl ReadStorage for MockStorage { type Repository = MockRepository; fn info(&self) -> &crate::git::UserInfo { &self.info } fn path(&self) -> &Path { self.path.as_path() } fn path_of(&self, rid: &RepoId) -> PathBuf { self.path().join(rid.canonical()) } fn contains(&self, rid: &RepoId) -> Result { Ok(self.repos.contains_key(rid)) } fn repository(&self, rid: RepoId) -> Result { self.repos .get(&rid) .ok_or_else(|| { RepositoryError::Storage(Error::Io(io::Error::from(io::ErrorKind::NotFound))) }) .cloned() } fn repositories(&self) -> Result, Error> { Ok(self .repos .iter() .map(|(rid, r)| RepositoryInfo { rid: *rid, head: r.head().unwrap().1, doc: r.doc.clone().into(), refs: None, synced_at: None, }) .collect()) } } impl WriteStorage for MockStorage { type RepositoryMut = MockRepository; fn repository_mut(&self, rid: RepoId) -> Result { self.repos .get(&rid) .ok_or(RepositoryError::Storage(Error::Io(io::Error::from( io::ErrorKind::NotFound, )))) .cloned() } fn create(&self, _rid: RepoId) -> Result { todo!() } fn clean(&self, _rid: RepoId) -> Result, RepositoryError> { todo!() } } #[derive(Clone, Debug)] pub struct MockRepository { pub id: RepoId, pub doc: DocAt, pub remotes: HashMap, } impl MockRepository { pub fn new(id: RepoId, doc: Doc) -> Self { let (blob, _) = doc.encode().unwrap(); Self { id, doc: DocAt { commit: Oid::from_str("ffffffffffffffffffffffffffffffffffffffff").unwrap(), blob, doc, }, remotes: HashMap::default(), } } } impl self::refs::sigrefs::git::reference::Reader for MockRepository { fn find_reference( &self, reference: &git::fmt::Namespaced, ) -> Result, refs::sigrefs::git::reference::error::FindReference> { use refs::sigrefs::git::reference::error::FindReference; let ns = reference.namespace(); let remote: PublicKey = ns.as_str().parse().map_err(FindReference::other)?; let reference = reference.strip_namespace(); match self.remotes.get(&remote) { None => Ok(None), Some(refs) => { if reference == *refs::SIGREFS_BRANCH { Ok(Some(refs.at)) } else { Ok(refs.sigrefs.get(&reference)) } } } } } impl RemoteRepository for MockRepository { fn remote(&self, id: &RemoteId) -> Result, refs::Error> { self.remotes .get(id) .map(|refs| Remote { refs: refs.sigrefs.clone(), }) .ok_or(refs::Error::InvalidRef) } fn remotes(&self) -> Result, refs::Error> { Ok(self .remotes .iter() .map(|(id, refs)| { ( *id, Remote { refs: refs.sigrefs.clone(), }, ) }) .collect()) } fn remote_refs_at(&self) -> Result, refs::Error> { Ok(self .remotes .values() .map(|s| refs::RefsAt { remote: s.id(), at: s.at, }) .collect()) } } impl ValidateRepository for MockRepository { fn validate_remote(&self, _remote: &Remote) -> Result { Ok(Validations::default()) } } impl ReadRepository for MockRepository { fn id(&self) -> RepoId { self.id } fn is_empty(&self) -> Result { Ok(self.remotes.is_empty()) } fn head(&self) -> Result<(fmt::Qualified<'_>, Oid), RepositoryError> { Ok((fmt::qualified!("refs/heads/master"), arbitrary::oid())) } fn canonical_head(&self) -> Result<(fmt::Qualified<'_>, Oid), RepositoryError> { todo!() } fn path(&self) -> &std::path::Path { todo!() } fn commit(&self, oid: Oid) -> Result, git::raw::Error> { Err(git::raw::Error::new( git::raw::ErrorCode::NotFound, git::raw::ErrorClass::None, format!("commit {oid} not found"), )) } fn revwalk(&self, _head: Oid) -> Result, git::raw::Error> { todo!() } fn contains(&self, oid: Oid) -> Result { Ok(self .remotes .values() .any(|sigrefs| sigrefs.at == oid || sigrefs.values().any(|oid_| *oid_ == oid))) } fn is_ancestor_of(&self, _ancestor: Oid, _head: Oid) -> Result { Ok(true) } fn blob(&self, _oid: Oid) -> Result, git::raw::Error> { todo!() } fn blob_at>( &self, _oid: Oid, _path: P, ) -> Result, git::raw::Error> { todo!() } fn reference( &self, _remote: &RemoteId, _reference: &git::fmt::Qualified, ) -> Result, git::raw::Error> { todo!() } fn reference_oid( &self, remote: &RemoteId, reference: &crate::git::fmt::Qualified, ) -> Result { let not_found = || { crate::git::raw::Error::new( crate::git::raw::ErrorCode::NotFound, crate::git::raw::ErrorClass::Reference, format!("could not find {reference} for {remote}"), ) }; let refs = self.remotes.get(remote).ok_or_else(not_found)?; if reference == &*refs::SIGREFS_BRANCH { Ok(refs.at) } else { refs.sigrefs.get(reference).ok_or_else(not_found) } } fn references_of(&self, _remote: &RemoteId) -> Result { todo!() } fn references_glob( &self, _pattern: &crate::git::fmt::refspec::PatternStr, ) -> Result, Oid)>, crate::git::raw::Error> { todo!() } fn identity_doc(&self) -> Result { Ok(self.doc.clone()) } fn identity_doc_at(&self, _head: Oid) -> Result { Ok(self.doc.clone()) } fn identity_head(&self) -> Result { self.canonical_identity_head() } fn identity_head_of(&self, _remote: &RemoteId) -> Result { Ok(self.doc.commit) } fn identity_root(&self) -> Result { Ok(self.doc.commit) } fn identity_root_of(&self, _remote: &RemoteId) -> Result { Ok(self.doc.commit) } fn canonical_identity_head(&self) -> Result { Ok(self.doc.commit) } fn merge_base(&self, _left: &Oid, _right: &Oid) -> Result { todo!() } } impl WriteRepository for MockRepository { fn raw(&self) -> &git::raw::Repository { todo!() } fn set_head(&self) -> Result { todo!() } fn set_identity_head_to(&self, _commit: Oid) -> Result<(), RepositoryError> { todo!() } fn set_remote_identity_root_to( &self, _remote: &RemoteId, _root: Oid, ) -> Result<(), RepositoryError> { todo!() } fn set_user(&self, _info: &crate::git::UserInfo) -> Result<(), Error> { todo!() } } impl SignRepository for MockRepository { fn sign_refs>( &self, _signer: &Device, ) -> Result, RepositoryError> { todo!() } } impl radicle_cob::Store for MockRepository {} impl radicle_cob::object::Storage for MockRepository { type ObjectsError = Infallible; type TypesError = Infallible; type UpdateError = Infallible; type RemoveError = Infallible; type Namespace = crypto::PublicKey; fn objects( &self, _typename: &radicle_cob::TypeName, _object_id: &radicle_cob::ObjectId, ) -> Result { todo!() } fn types( &self, _typename: &radicle_cob::TypeName, ) -> Result< std::collections::BTreeMap, Self::TypesError, > { todo!() } fn update( &self, _namespace: &Self::Namespace, _typename: &radicle_cob::TypeName, _object_id: &radicle_cob::ObjectId, _entry: &radicle_cob::EntryId, ) -> Result<(), Self::UpdateError> { todo!() } fn remove( &self, _namespace: &Self::Namespace, _typename: &radicle_cob::TypeName, _object_id: &radicle_cob::ObjectId, ) -> Result<(), Self::RemoveError> { todo!() } } impl radicle_cob::change::Storage for MockRepository { type StoreError = radicle_cob::git::change::error::Create; type LoadError = radicle_cob::git::change::error::Load; type ObjectId = Oid; type Parent = Oid; type Signatures = radicle_cob::signatures::ExtendedSignature; fn store( &self, _resource: Option, _related: Vec, _signer: &G, _template: radicle_cob::change::Template, ) -> Result< radicle_cob::change::store::Entry, Self::StoreError, > where G: radicle_crypto::signature::Signer, { todo!() } fn load( &self, _id: Self::ObjectId, ) -> Result< radicle_cob::change::store::Entry, Self::LoadError, > { todo!() } fn parents_of(&self, _id: &Oid) -> Result, Self::LoadError> { todo!() } fn manifest_of(&self, _id: &Oid) -> Result { todo!() } }