use std::str::FromStr;
use radicle::{
cob::patch::{Patch, PatchId, cache::Patches},
git::Oid,
identity::{Project, RepoId},
profile::Profile,
storage::{ReadStorage, RepositoryInfo, git::Repository},
};
pub struct Radicle {
profile: Profile,
}
impl Radicle {
pub fn new() -> Result<Self, ErgoError> {
Ok(Self {
profile: Profile::load().map_err(ErgoError::load_profile)?,
})
}
pub fn profile(&self) -> &Profile {
&self.profile
}
pub fn repositories(&self) -> Result<Vec<RepositoryInfo>, ErgoError> {
self.profile
.storage
.repositories()
.map_err(ErgoError::list_repositories)
}
pub fn repository(&self, repo_id: &RepoId) -> Result<Repository, ErgoError> {
self.profile
.storage
.repository(*repo_id)
.map_err(|err| ErgoError::load_repository(repo_id, err))
}
pub fn repository_by_name(&self, wanted: &str) -> Result<Repository, ErgoError> {
let matching: Result<Vec<RepoId>, ErgoError> = self
.repositories()?
.iter()
.filter_map(|ri| match self.project(&ri.rid) {
Ok(project) if project.name() == wanted => Some(Ok(ri.rid)),
Err(err) => Some(Err(err)),
_ => None,
})
.collect();
let matching = matching?;
match matching[..] {
[] => Err(ErgoError::no_repository_with_name(wanted)),
[_] => {
let repo_id = matching[0];
let repo = self.repository(&repo_id)?;
Ok(repo)
}
[_, _, ..] => Err(ErgoError::name_is_not_unique(wanted)),
}
}
pub fn project(&self, repo_id: &RepoId) -> Result<Project, ErgoError> {
let repo = self.repository(repo_id)?;
repo.project()
.map_err(|err| ErgoError::load_project(repo_id, err))
}
pub fn patches(&self, repo_id: &RepoId) -> Result<Vec<(PatchId, Patch)>, ErgoError> {
let repo = self.repository(repo_id)?;
let patches = self
.profile
.home
.patches(&repo)
.map_err(|err| ErgoError::load_patches(repo_id, err))?;
let mut items = vec![];
let list = patches
.list()
.map_err(|err| ErgoError::list_cache(repo_id, err))?;
for result in list {
let (id, patch) = result.map_err(|err| ErgoError::cache_list_item(repo_id, err))?;
items.push((id, patch));
}
Ok(items)
}
pub fn patch(&self, repo_id: &RepoId, patch_id: &PatchId) -> Result<Patch, ErgoError> {
let repo = self.repository(repo_id)?;
let patches = self
.profile
.home
.patches(&repo)
.map_err(|err| ErgoError::LoadPatches(*repo_id, Box::new(err)))?;
patches
.get(patch_id)
.map_err(|err| ErgoError::get_patch(repo_id, patch_id, err))?
.ok_or(ErgoError::no_such_patch(repo_id, patch_id))
}
pub fn resolve_patch_id(&self, repo_id: &RepoId, id: &str) -> Result<PatchId, ErgoError> {
let repo = self.repository(repo_id)?;
let object = repo
.backend
.revparse_single(id)
.map_err(|err| ErgoError::resolve_patch_id(id, err))?;
Ok(PatchId::from(object.id()))
}
pub fn resolve_commit(&self, repo_id: &RepoId, gitref: &str) -> Result<Oid, ErgoError> {
if let Ok(oid) = Oid::from_str(gitref) {
Ok(oid)
} else {
let repo = self.repository(repo_id)?;
let object = repo
.backend
.revparse_single(gitref)
.map_err(|err| ErgoError::resolve_commit(gitref, repo_id, err))?;
Ok(Oid::from(object.id()))
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum ErgoError {
#[error("failed to load Radicle profile")]
LoadProfile(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to list repositories in Radicle node storage")]
ListRepositories(#[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to load info from Radicle node storage for repository {0}")]
LoadRepo(RepoId, #[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to load project info from Radicle node storage for repository {0}")]
LoadProject(RepoId, #[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to load patch list from Radicle node storage for repository {0}")]
LoadPatches(RepoId, #[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to list patches for repository {0}")]
ListCache(RepoId, #[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to list info for patch {0}")]
CacheListItem(RepoId, #[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to resolve patch id {0:?} in repository {1}")]
ResolvePatchId(String, #[source] Box<dyn std::error::Error + Send + Sync>),
#[error("failed to resolve commit id {0:?} in repository {1}")]
ResolveCommit(
String,
RepoId,
#[source] Box<dyn std::error::Error + Send + Sync>,
),
#[error("failed to load patch {1} from Radicle node storage for repository {0}")]
GetPatch(
RepoId,
PatchId,
#[source] Box<dyn std::error::Error + Send + Sync>,
),
#[error("no patch {1} in repository {0}")]
NoSuchPatch(RepoId, PatchId),
#[error("no repository called {0:?}")]
NoRepositoryWithName(String),
#[error("repository name is not unique: {0:?}")]
NameIsNotUnique(String),
}
impl ErgoError {
pub(crate) fn load_profile(err: radicle::profile::Error) -> Self {
Self::LoadProfile(Box::new(err))
}
pub(crate) fn list_repositories(err: radicle::storage::Error) -> Self {
Self::ListRepositories(Box::new(err))
}
pub(crate) fn load_repository(id: &RepoId, err: radicle::storage::RepositoryError) -> Self {
Self::LoadRepo(*id, Box::new(err))
}
pub(crate) fn load_project(id: &RepoId, err: radicle::storage::RepositoryError) -> Self {
Self::LoadRepo(*id, Box::new(err))
}
pub(crate) fn load_patches(id: &RepoId, err: radicle::profile::Error) -> Self {
Self::LoadPatches(*id, Box::new(err))
}
pub(crate) fn list_cache(id: &RepoId, err: radicle::patch::cache::Error) -> Self {
Self::ListCache(*id, Box::new(err))
}
pub(crate) fn cache_list_item(id: &RepoId, err: radicle::patch::cache::Error) -> Self {
Self::CacheListItem(*id, Box::new(err))
}
pub(crate) fn resolve_patch_id<S: Into<String>>(
id: S,
err: radicle::storage::git::raw::Error,
) -> Self {
Self::ResolvePatchId(id.into(), Box::new(err))
}
pub(crate) fn resolve_commit<S: Into<String>>(
commit: S,
repo: &RepoId,
err: radicle::storage::git::raw::Error,
) -> Self {
Self::ResolveCommit(commit.into(), *repo, Box::new(err))
}
pub(crate) fn get_patch(
repo: &RepoId,
patch: &PatchId,
err: radicle::patch::cache::Error,
) -> Self {
Self::GetPatch(*repo, *patch, Box::new(err))
}
pub(crate) fn no_such_patch(repo: &RepoId, patch: &PatchId) -> Self {
Self::NoSuchPatch(*repo, *patch)
}
pub(crate) fn no_repository_with_name<S: Into<String>>(name: S) -> Self {
Self::NoRepositoryWithName(name.into())
}
pub(crate) fn name_is_not_unique<S: Into<String>>(name: S) -> Self {
Self::NameIsNotUnique(name.into())
}
}