#![deny(clippy::unwrap_used)]
pub mod cache;
pub mod common;
pub mod external;
pub mod identity;
pub mod issue;
pub mod op;
pub mod patch;
pub mod store;
pub mod stream;
pub mod thread;
#[cfg(test)]
pub mod test;
#[cfg(test)]
pub use radicle_cob::stable;
pub use cache::{MigrateCallback, migrate};
pub use common::*;
pub use op::{ActorId, Op};
pub use radicle_cob::{
CollaborativeObject, Contents, Create, Embed, Entry, Evaluate, History, Manifest, ObjectId,
Store, TypeName, Update, Updated, Version, change, history::EntryId, object,
object::collaboration::error, type_name::TypeNameParse,
};
pub use radicle_cob::{create, get, list, remove, update};
/// The exact identifier for a particular COB.
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
pub struct TypedId {
/// The identifier of the COB in the store.
pub id: ObjectId,
/// The type identifier of the COB in the store.
pub type_name: TypeName,
}
impl std::fmt::Display for TypedId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}/{}", self.type_name, self.id)
}
}
/// Errors that occur when parsing a Git refname into a [`TypedId`].
#[derive(Debug, thiserror::Error)]
pub enum ParseIdentifierError {
#[error(transparent)]
TypeName(#[from] TypeNameParse),
#[error(transparent)]
ObjectId(#[from] object::ParseObjectId),
}
impl TypedId {
/// Returns `true` is the [`TypedId::type_name`] is for an
/// [`issue::Issue`].
pub fn is_issue(&self) -> bool {
self.type_name == *issue::TYPENAME
}
/// Returns `true` is the [`TypedId::type_name`] is for an
/// [`patch::Patch`].
pub fn is_patch(&self) -> bool {
self.type_name == *patch::TYPENAME
}
/// Returns `true` is the [`TypedId::type_name`] is for an
/// [`identity::Identity`].
pub fn is_identity(&self) -> bool {
self.type_name == *identity::TYPENAME
}
/// Parse a [`crate::git::fmt::Namespaced`] refname into a [`TypedId`].
///
/// All namespaces are stripped before parsing the suffix for the
/// [`TypedId`] (see [`TypedId::from_qualified`]).
pub fn from_namespaced(
n: &crate::git::fmt::Namespaced,
) -> Result<Option<Self>, ParseIdentifierError> {
Self::from_qualified(&n.strip_namespace_recursive())
}
/// Parse a [`crate::git::fmt::Qualified`] refname into a [`TypedId`].
///
/// The refname is expected to be of the form:
/// `refs/cobs/<type name>/<object id>`
///
/// If the refname is not of that form then `None` will be returned.
///
/// # Errors
///
/// This will fail if the refname is of the correct form, but the
/// type name or object id fail to parse.
pub fn from_qualified(
q: &crate::git::fmt::Qualified,
) -> Result<Option<Self>, ParseIdentifierError> {
match q.non_empty_iter() {
("refs", "cobs", type_name, mut id) => {
let Some(id) = id.next() else {
return Ok(None);
};
Ok(Some(Self {
id: id.parse()?,
type_name: type_name.parse()?,
}))
}
_ => Ok(None),
}
}
}