use crate::{ ext::{ObjectIdExt, ReferenceExt}, head::Kind, Head, }; mod error { use crate::{object, reference}; /// The error returned by [`Head::peel_to_id_in_place()`](super::Head::try_peel_to_id_in_place()) /// and [`Head::into_fully_peeled_id()`](super::Head::try_into_peeled_id()). #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[error(transparent)] FindExistingObject(#[from] object::find::existing::Error), #[error(transparent)] PeelReference(#[from] reference::peel::Error), } } pub use error::Error; /// pub mod into_id { use crate::object; /// The error returned by [`Head::into_peeled_id()`](super::Head::into_peeled_id()). #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[error(transparent)] Peel(#[from] super::Error), #[error("Branch '{name}' does not have any commits")] Unborn { name: gix_ref::FullName }, #[error(transparent)] ObjectKind(#[from] object::try_into::Error), } } /// pub mod to_commit { use crate::object; /// The error returned by [`Head::peel_to_commit_in_place()`](super::Head::peel_to_commit_in_place()). #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[error(transparent)] PeelToObject(#[from] super::to_object::Error), #[error(transparent)] ObjectKind(#[from] object::try_into::Error), } } /// pub mod to_object { /// The error returned by [`Head::peel_to_object_in_place()`](super::Head::peel_to_object_in_place()). #[derive(Debug, thiserror::Error)] #[allow(missing_docs)] pub enum Error { #[error(transparent)] Peel(#[from] super::Error), #[error("Branch '{name}' does not have any commits")] Unborn { name: gix_ref::FullName }, } } impl<'repo> Head<'repo> { /// Peel this instance and consume it to make obtaining its final target id possible, while returning an error on unborn heads. /// /// The final target is obtained by following symbolic references and peeling tags to their final destination, which /// typically is a commit, but can be any object. pub fn into_peeled_id(mut self) -> Result, into_id::Error> { self.try_peel_to_id_in_place()?; self.id().ok_or_else(|| match self.kind { Kind::Symbolic(gix_ref::Reference { name, .. }) | Kind::Unborn(name) => into_id::Error::Unborn { name }, Kind::Detached { .. } => unreachable!("id can be returned after peeling"), }) } /// Peel this instance and consume it to make obtaining its final target object possible, while returning an error on unborn heads. /// /// The final target is obtained by following symbolic references and peeling tags to their final destination, which /// typically is a commit, but can be any object as well. pub fn into_peeled_object(mut self) -> Result, to_object::Error> { self.peel_to_object_in_place() } /// Consume this instance and transform it into the final object that it points to, or `Ok(None)` if the `HEAD` /// reference is yet to be born. /// /// The final target is obtained by following symbolic references and peeling tags to their final destination, which /// typically is a commit, but can be any object. pub fn try_into_peeled_id(mut self) -> Result>, Error> { self.try_peel_to_id_in_place() } /// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no /// more object to follow, and return that object id. /// /// Returns `Ok(None)` if the head is unborn. /// /// The final target is obtained by following symbolic references and peeling tags to their final destination, which /// typically is a commit, but can be any object. pub fn try_peel_to_id_in_place(&mut self) -> Result>, Error> { Ok(Some(match &mut self.kind { Kind::Unborn(_name) => return Ok(None), Kind::Detached { peeled: Some(peeled), .. } => (*peeled).attach(self.repo), Kind::Detached { peeled: None, target } => { let id = target.attach(self.repo); if id.header()?.kind() == gix_object::Kind::Commit { id } else { match id.object()?.peel_tags_to_end() { Ok(obj) => { self.kind = Kind::Detached { peeled: Some(obj.id), target: *target, }; obj.id() } Err(err) => return Err(err.into()), } } } Kind::Symbolic(r) => { let mut nr = r.clone().attach(self.repo); let peeled = nr.peel_to_id_in_place(); *r = nr.detach(); peeled? } })) } /// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no /// more object to follow, transform the id into a commit if possible and return that. /// /// Returns an error if the head is unborn or if it doesn't point to a commit. pub fn peel_to_object_in_place(&mut self) -> Result, to_object::Error> { let id = self .try_peel_to_id_in_place()? .ok_or_else(|| to_object::Error::Unborn { name: self.referent_name().expect("unborn").to_owned(), })?; id.object() .map_err(|err| to_object::Error::Peel(Error::FindExistingObject(err))) } /// Follow the symbolic reference of this head until its target object and peel it by following tag objects until there is no /// more object to follow, transform the id into a commit if possible and return that. /// /// Returns an error if the head is unborn or if it doesn't point to a commit. pub fn peel_to_commit_in_place(&mut self) -> Result, to_commit::Error> { Ok(self.peel_to_object_in_place()?.try_into_commit()?) } }