use std::collections::{BTreeSet, HashMap}; use std::convert::Infallible; use std::io; use std::path::{Path, PathBuf}; use std::str::FromStr; use git_ext::ref_format as fmt; use crate::crypto::{Signer, Verified}; use crate::identity::doc::{Doc, DocAt, DocError, RepoId}; use crate::node::NodeId; pub use crate::storage::*; use super::{arbitrary, fixtures}; #[derive(Clone, Debug)] pub struct MockStorage { pub path: PathBuf, pub info: 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 empty() -> Self { Self::new(Vec::new()) } } impl ReadStorage for MockStorage { type Repository = MockRepository; fn info(&self) -> &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 inventory(&self) -> Result { Ok(self.repos.keys().cloned().collect::>()) } fn insert(&self, _rid: RepoId) {} 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, }) .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 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 { Err(git_ext::Error::NotFound(git_ext::NotFound::NoSuchObject( *oid, ))) } fn revwalk(&self, _head: Oid) -> Result { todo!() } fn contains(&self, oid: Oid) -> Result { Ok(self .remotes .values() .any(|sigrefs| sigrefs.at == oid || sigrefs.refs.values().any(|oid_| *oid_ == oid))) } fn is_ancestor_of(&self, _ancestor: Oid, _head: Oid) -> Result { Ok(true) } fn blob(&self, _oid: Oid) -> Result { todo!() } fn blob_at>( &self, _oid: git_ext::Oid, _path: P, ) -> Result { todo!() } fn reference( &self, _remote: &RemoteId, _reference: &git::Qualified, ) -> Result { todo!() } fn reference_oid( &self, remote: &RemoteId, reference: &git::Qualified, ) -> Result { let not_found = || { git::raw::Error::new( git::raw::ErrorCode::NotFound, 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: &git::PatternStr, ) -> Result, git::ext::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 { todo!() } fn identity_root(&self) -> Result { todo!() } fn identity_root_of(&self, _remote: &RemoteId) -> Result { todo!() } fn canonical_identity_head(&self) -> Result { Ok(Oid::from_str("cccccccccccccccccccccccccccccccccccccccc").unwrap()) } fn merge_base(&self, _left: &Oid, _right: &Oid) -> Result { todo!() } } impl WriteRepository for MockRepository { fn raw(&self) -> &git2::Repository { todo!() } fn set_head(&self) -> Result { todo!() } fn set_identity_head_to(&self, _commit: Oid) -> Result<(), RepositoryError> { todo!() } fn set_user(&self, _info: &git::UserInfo) -> Result<(), Error> { todo!() } } impl SignRepository for MockRepository { fn sign_refs( &self, _signer: &G, ) -> Result, Error> { 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; 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, _identifier: &radicle_crypto::PublicKey, _typename: &radicle_cob::TypeName, _object_id: &radicle_cob::ObjectId, _entry: &radicle_cob::EntryId, ) -> Result<(), Self::UpdateError> { todo!() } fn remove( &self, _identifier: &radicle_crypto::PublicKey, _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::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!() } }