Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 42 additions & 25 deletions gitoxide-core/src/repository/revision/explain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ use gix::{
Delegate,
},
},
Exn,
};

pub fn explain(spec: std::ffi::OsString, mut out: impl std::io::Write) -> anyhow::Result<()> {
let mut explain = Explain::new(&mut out);
let spec = gix::path::os_str_into_bstr(&spec)?;
gix::revision::plumbing::spec::parse(spec, &mut explain)?;
gix::revision::plumbing::spec::parse(spec, &mut explain).map_err(gix::Error::from)?;
if let Some(err) = explain.err {
bail!(err);
}
Expand All @@ -41,9 +42,10 @@ impl<'a> Explain<'a> {
err: None,
}
}
fn prefix(&mut self) -> Option<()> {
fn prefix(&mut self) -> Result<(), Exn> {
self.call += 1;
write!(self.out, "{:02}. ", self.call).ok()
write!(self.out, "{:02}. ", self.call).ok();
Ok(())
}
fn revision_name(&self) -> BString {
self.ref_name.clone().unwrap_or_else(|| {
Expand All @@ -56,13 +58,18 @@ impl<'a> Explain<'a> {
}

impl delegate::Revision for Explain<'_> {
fn find_ref(&mut self, name: &BStr) -> Option<()> {
fn find_ref(&mut self, name: &BStr) -> Result<(), Exn> {
self.prefix()?;
self.ref_name = Some(name.into());
writeln!(self.out, "Lookup the '{name}' reference").ok()
writeln!(self.out, "Lookup the '{name}' reference").ok();
Ok(())
}

fn disambiguate_prefix(&mut self, prefix: gix::hash::Prefix, hint: Option<delegate::PrefixHint<'_>>) -> Option<()> {
fn disambiguate_prefix(
&mut self,
prefix: gix::hash::Prefix,
hint: Option<delegate::PrefixHint<'_>>,
) -> Result<(), Exn> {
self.prefix()?;
self.oid_prefix = Some(prefix);
writeln!(
Expand All @@ -76,10 +83,11 @@ impl delegate::Revision for Explain<'_> {
format!("commit {generation} generations in future of reference {ref_name:?}"),
}
)
.ok()
.ok();
Ok(())
}

fn reflog(&mut self, query: ReflogLookup) -> Option<()> {
fn reflog(&mut self, query: ReflogLookup) -> Result<(), Exn> {
self.prefix()?;
self.has_implicit_anchor = true;
let ref_name: &BStr = self.ref_name.as_ref().map_or_else(|| "HEAD".into(), AsRef::as_ref);
Expand All @@ -92,16 +100,18 @@ impl delegate::Revision for Explain<'_> {
ref_name
)
.ok(),
}
};
Ok(())
}

fn nth_checked_out_branch(&mut self, branch_no: usize) -> Option<()> {
fn nth_checked_out_branch(&mut self, branch_no: usize) -> Result<(), Exn> {
self.prefix()?;
self.has_implicit_anchor = true;
writeln!(self.out, "Find the {branch_no}th checked-out branch of 'HEAD'").ok()
writeln!(self.out, "Find the {branch_no}th checked-out branch of 'HEAD'").ok();
Ok(())
}

fn sibling_branch(&mut self, kind: SiblingBranch) -> Option<()> {
fn sibling_branch(&mut self, kind: SiblingBranch) -> Result<(), Exn> {
self.prefix()?;
self.has_implicit_anchor = true;
let ref_info = match self.ref_name.as_ref() {
Expand All @@ -117,12 +127,13 @@ impl delegate::Revision for Explain<'_> {
},
ref_info
)
.ok()
.ok();
Ok(())
}
}

impl delegate::Navigate for Explain<'_> {
fn traverse(&mut self, kind: Traversal) -> Option<()> {
fn traverse(&mut self, kind: Traversal) -> Result<(), Exn> {
self.prefix()?;
let name = self.revision_name();
writeln!(
Expand All @@ -133,10 +144,11 @@ impl delegate::Navigate for Explain<'_> {
Traversal::NthParent(no) => format!("Select the {no}. parent of revision named '{name}'"),
}
)
.ok()
.ok();
Ok(())
}

fn peel_until(&mut self, kind: PeelTo<'_>) -> Option<()> {
fn peel_until(&mut self, kind: PeelTo<'_>) -> Result<(), Exn> {
self.prefix()?;
writeln!(
self.out,
Expand All @@ -148,10 +160,11 @@ impl delegate::Navigate for Explain<'_> {
PeelTo::Path(path) => format!("Lookup the object at '{path}' from the current tree-ish"),
}
)
.ok()
.ok();
Ok(())
}

fn find(&mut self, regex: &BStr, negated: bool) -> Option<()> {
fn find(&mut self, regex: &BStr, negated: bool) -> Result<(), Exn> {
self.prefix()?;
self.has_implicit_anchor = true;
let negate_text = if negated { "does not match" } else { "matches" };
Expand All @@ -172,10 +185,11 @@ impl delegate::Navigate for Explain<'_> {
),
}
)
.ok()
.ok();
Ok(())
}

fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()> {
fn index_lookup(&mut self, path: &BStr, stage: u8) -> Result<(), Exn> {
self.prefix()?;
self.has_implicit_anchor = true;
writeln!(
Expand All @@ -190,12 +204,13 @@ impl delegate::Navigate for Explain<'_> {
_ => unreachable!("BUG: parser assures of that"),
}
)
.ok()
.ok();
Ok(())
}
}

impl delegate::Kind for Explain<'_> {
fn kind(&mut self, kind: spec::Kind) -> Option<()> {
fn kind(&mut self, kind: spec::Kind) -> Result<(), Exn> {
self.prefix()?;
self.call = 0;
writeln!(
Expand All @@ -211,14 +226,16 @@ impl delegate::Kind for Explain<'_> {
unreachable!("BUG: 'single' mode is implied but cannot be set explicitly"),
}
)
.ok()
.ok();
Ok(())
}
}

impl Delegate for Explain<'_> {
fn done(&mut self) {
fn done(&mut self) -> Result<(), Exn> {
if !self.has_implicit_anchor && self.ref_name.is_none() && self.oid_prefix.is_none() {
self.err = Some("Incomplete specification lacks its anchor, like a reference or object name".into());
}
};
Ok(())
}
}
55 changes: 55 additions & 0 deletions gix-error/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
use crate::{exn, Error, Exn};
use std::fmt::Formatter;

pub(super) enum Inner {
Boxed(Box<dyn std::error::Error + Send + Sync>),
Exn(Box<exn::Frame>),
}

impl Error {
/// Create a new instance representing the given `error`.
pub fn from_error(error: impl std::error::Error + Send + Sync + 'static) -> Self {
Error {
inner: Inner::Boxed(Box::new(error)),
}
}
}

impl std::fmt::Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.inner {
Inner::Boxed(err) => std::fmt::Display::fmt(&*err, f),
Inner::Exn(frame) => std::fmt::Display::fmt(frame, f),
}
}
}

impl std::fmt::Debug for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.inner {
Inner::Boxed(err) => std::fmt::Debug::fmt(&*err, f),
Inner::Exn(frame) => std::fmt::Debug::fmt(frame, f),
}
}
}

impl std::error::Error for Error {
/// Return the first source of an [Exn](crate::Exn) error, or the source of a boxed error.
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.inner {
Inner::Boxed(err) => err.source(),
Inner::Exn(frame) => frame.children().first().map(|f| f.as_error()),
}
}
}

impl<E> From<Exn<E>> for Error
where
E: std::error::Error + Send + Sync + 'static,
{
fn from(err: Exn<E>) -> Self {
Error {
inner: Inner::Exn(err.into()),
}
}
}
6 changes: 4 additions & 2 deletions gix-error/src/exn/debug.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@ impl fmt::Debug for Frame {

fn write_exn(f: &mut Formatter<'_>, frame: &Frame, level: usize, prefix: &str) -> fmt::Result {
write!(f, "{}", frame.as_error())?;
write_location(f, frame)?;
if !f.alternate() {
write_location(f, frame)?;
}

let children = frame.children();
let children_len = children.len();

for (i, child) in children.iter().enumerate() {
write!(f, "\n{prefix}|")?;
write!(f, "\n{prefix}|-> ")?;
write!(f, "\n{prefix}└─ ")?;

let child_child_len = child.children().len();
if level == 0 && children_len == 1 && child_child_len == 1 {
Expand Down
8 changes: 7 additions & 1 deletion gix-error/src/exn/display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@

use std::fmt;

use crate::exn::Exn;
use crate::exn::{Exn, Frame};

impl<E: std::error::Error + Send + Sync + 'static> fmt::Display for Exn<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_error())
}
}

impl fmt::Display for Frame {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_error())
}
}
Loading