Skip to content

Commit 547c8df

Browse files
committed
Actually introduce gix-error into gix-revision.
- Add a `Message` error to `gix-error` for general use.
1 parent 2647b9d commit 547c8df

File tree

44 files changed

+1648
-1061
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1648
-1061
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gitoxide-core/src/repository/diff.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use gix::{
88
prelude::ObjectIdExt,
99
ObjectId,
1010
};
11+
use std::path::PathBuf;
1112

1213
pub fn tree(
1314
mut repo: gix::Repository,
@@ -125,15 +126,18 @@ fn resolve_revspec(
125126
let result = repo.rev_parse(revspec.as_bstr());
126127

127128
match result {
128-
Err(gix::revision::spec::parse::Error::FindReference(gix::refs::file::find::existing::Error::NotFound {
129-
name,
130-
})) => {
131-
let root = repo.workdir().map(ToOwned::to_owned);
132-
let name = gix::path::os_string_into_bstring(name.into())?;
133-
134-
Ok((ObjectId::null(gix::hash::Kind::Sha1), root, name))
129+
Err(err) => {
130+
// TODO: finish
131+
let reference_not_found_error = None::<PathBuf>;
132+
if let Some(name) = reference_not_found_error {
133+
let root = repo.workdir().map(ToOwned::to_owned);
134+
let name = gix::path::os_string_into_bstring(name.into())?;
135+
136+
Ok((ObjectId::null(gix::hash::Kind::Sha1), root, name))
137+
} else {
138+
Err(err.into())
139+
}
135140
}
136-
Err(err) => Err(err.into()),
137141
Ok(resolved_revspec) => {
138142
let blob_id = resolved_revspec
139143
.single()

gitoxide-core/src/repository/revision/explain.rs

Lines changed: 41 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ use gix::{
99
Delegate,
1010
},
1111
},
12+
Exn,
1213
};
1314

1415
pub fn explain(spec: std::ffi::OsString, mut out: impl std::io::Write) -> anyhow::Result<()> {
1516
let mut explain = Explain::new(&mut out);
1617
let spec = gix::path::os_str_into_bstr(&spec)?;
17-
gix::revision::plumbing::spec::parse(spec, &mut explain)?;
18+
gix::revision::plumbing::spec::parse(spec, &mut explain).map_err(gix::Error::from)?;
1819
if let Some(err) = explain.err {
1920
bail!(err);
2021
}
@@ -41,9 +42,10 @@ impl<'a> Explain<'a> {
4142
err: None,
4243
}
4344
}
44-
fn prefix(&mut self) -> Option<()> {
45+
fn prefix(&mut self) -> Result<(), Exn> {
4546
self.call += 1;
46-
write!(self.out, "{:02}. ", self.call).ok()
47+
write!(self.out, "{:02}. ", self.call).ok();
48+
Ok(())
4749
}
4850
fn revision_name(&self) -> BString {
4951
self.ref_name.clone().unwrap_or_else(|| {
@@ -56,13 +58,18 @@ impl<'a> Explain<'a> {
5658
}
5759

5860
impl delegate::Revision for Explain<'_> {
59-
fn find_ref(&mut self, name: &BStr) -> Option<()> {
61+
fn find_ref(&mut self, name: &BStr) -> Result<(), Exn> {
6062
self.prefix()?;
6163
self.ref_name = Some(name.into());
62-
writeln!(self.out, "Lookup the '{name}' reference").ok()
64+
writeln!(self.out, "Lookup the '{name}' reference").ok();
65+
Ok(())
6366
}
6467

65-
fn disambiguate_prefix(&mut self, prefix: gix::hash::Prefix, hint: Option<delegate::PrefixHint<'_>>) -> Option<()> {
68+
fn disambiguate_prefix(
69+
&mut self,
70+
prefix: gix::hash::Prefix,
71+
hint: Option<delegate::PrefixHint<'_>>,
72+
) -> Result<(), Exn> {
6673
self.prefix()?;
6774
self.oid_prefix = Some(prefix);
6875
writeln!(
@@ -76,10 +83,11 @@ impl delegate::Revision for Explain<'_> {
7683
format!("commit {generation} generations in future of reference {ref_name:?}"),
7784
}
7885
)
79-
.ok()
86+
.ok();
87+
Ok(())
8088
}
8189

82-
fn reflog(&mut self, query: ReflogLookup) -> Option<()> {
90+
fn reflog(&mut self, query: ReflogLookup) -> Result<(), Exn> {
8391
self.prefix()?;
8492
self.has_implicit_anchor = true;
8593
let ref_name: &BStr = self.ref_name.as_ref().map_or_else(|| "HEAD".into(), AsRef::as_ref);
@@ -92,16 +100,18 @@ impl delegate::Revision for Explain<'_> {
92100
ref_name
93101
)
94102
.ok(),
95-
}
103+
};
104+
Ok(())
96105
}
97106

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

104-
fn sibling_branch(&mut self, kind: SiblingBranch) -> Option<()> {
114+
fn sibling_branch(&mut self, kind: SiblingBranch) -> Result<(), Exn> {
105115
self.prefix()?;
106116
self.has_implicit_anchor = true;
107117
let ref_info = match self.ref_name.as_ref() {
@@ -117,12 +127,13 @@ impl delegate::Revision for Explain<'_> {
117127
},
118128
ref_info
119129
)
120-
.ok()
130+
.ok();
131+
Ok(())
121132
}
122133
}
123134

124135
impl delegate::Navigate for Explain<'_> {
125-
fn traverse(&mut self, kind: Traversal) -> Option<()> {
136+
fn traverse(&mut self, kind: Traversal) -> Result<(), Exn> {
126137
self.prefix()?;
127138
let name = self.revision_name();
128139
writeln!(
@@ -133,10 +144,11 @@ impl delegate::Navigate for Explain<'_> {
133144
Traversal::NthParent(no) => format!("Select the {no}. parent of revision named '{name}'"),
134145
}
135146
)
136-
.ok()
147+
.ok();
148+
Ok(())
137149
}
138150

139-
fn peel_until(&mut self, kind: PeelTo<'_>) -> Option<()> {
151+
fn peel_until(&mut self, kind: PeelTo<'_>) -> Result<(), Exn> {
140152
self.prefix()?;
141153
writeln!(
142154
self.out,
@@ -148,10 +160,11 @@ impl delegate::Navigate for Explain<'_> {
148160
PeelTo::Path(path) => format!("Lookup the object at '{path}' from the current tree-ish"),
149161
}
150162
)
151-
.ok()
163+
.ok();
164+
Ok(())
152165
}
153166

154-
fn find(&mut self, regex: &BStr, negated: bool) -> Option<()> {
167+
fn find(&mut self, regex: &BStr, negated: bool) -> Result<(), Exn> {
155168
self.prefix()?;
156169
self.has_implicit_anchor = true;
157170
let negate_text = if negated { "does not match" } else { "matches" };
@@ -172,10 +185,11 @@ impl delegate::Navigate for Explain<'_> {
172185
),
173186
}
174187
)
175-
.ok()
188+
.ok();
189+
Ok(())
176190
}
177191

178-
fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()> {
192+
fn index_lookup(&mut self, path: &BStr, stage: u8) -> Result<(), Exn> {
179193
self.prefix()?;
180194
self.has_implicit_anchor = true;
181195
writeln!(
@@ -190,12 +204,13 @@ impl delegate::Navigate for Explain<'_> {
190204
_ => unreachable!("BUG: parser assures of that"),
191205
}
192206
)
193-
.ok()
207+
.ok();
208+
Ok(())
194209
}
195210
}
196211

197212
impl delegate::Kind for Explain<'_> {
198-
fn kind(&mut self, kind: spec::Kind) -> Option<()> {
213+
fn kind(&mut self, kind: spec::Kind) -> Result<(), Exn> {
199214
self.prefix()?;
200215
self.call = 0;
201216
writeln!(
@@ -211,14 +226,16 @@ impl delegate::Kind for Explain<'_> {
211226
unreachable!("BUG: 'single' mode is implied but cannot be set explicitly"),
212227
}
213228
)
214-
.ok()
229+
.ok();
230+
Ok(())
215231
}
216232
}
217233

218234
impl Delegate for Explain<'_> {
219-
fn done(&mut self) {
235+
fn done(&mut self) -> Result<(), Exn> {
220236
if !self.has_implicit_anchor && self.ref_name.is_none() && self.oid_prefix.is_none() {
221237
self.err = Some("Incomplete specification lacks its anchor, like a reference or object name".into());
222238
}
239+
Ok(())
223240
}
224241
}

gix-error/src/error.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
use crate::{exn, Error, Exn};
2+
use std::fmt::Formatter;
3+
4+
/// Utilities
5+
impl Error {
6+
/// Return the first leaf error of this error tree or the error itself.
7+
pub fn leaf(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
8+
let mut current = self.inner.as_frame();
9+
while let Some(next) = current.children().first() {
10+
current = next;
11+
}
12+
current.as_error_send_sync()
13+
}
14+
//
15+
// /// Return an iterator over all sources, i.e. the linear chain.
16+
// pub fn iter_sources(&self) -> ErrorIter<'_> {
17+
// use std::error::Error;
18+
// ErrorIter { current: self.source() }
19+
// }
20+
}
21+
22+
pub(super) enum Inner {
23+
ExnAsError(Box<exn::Frame>),
24+
Exn(Box<exn::Frame>),
25+
}
26+
27+
impl Inner {
28+
fn as_frame(&self) -> &exn::Frame {
29+
match self {
30+
Inner::ExnAsError(f) | Inner::Exn(f) => f,
31+
}
32+
}
33+
}
34+
35+
impl Error {
36+
/// Create a new instance representing the given `error`.
37+
#[track_caller]
38+
pub fn from_error(error: impl std::error::Error + Send + Sync + 'static) -> Self {
39+
Error {
40+
inner: Inner::ExnAsError(Exn::new(error).into()),
41+
}
42+
}
43+
}
44+
45+
impl std::fmt::Display for Error {
46+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
47+
match &self.inner {
48+
Inner::ExnAsError(err) => std::fmt::Display::fmt(err.as_error(), f),
49+
Inner::Exn(frame) => std::fmt::Display::fmt(frame, f),
50+
}
51+
}
52+
}
53+
54+
impl std::fmt::Debug for Error {
55+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56+
match &self.inner {
57+
Inner::ExnAsError(err) => std::fmt::Debug::fmt(err.as_error(), f),
58+
Inner::Exn(frame) => std::fmt::Debug::fmt(frame, f),
59+
}
60+
}
61+
}
62+
63+
impl std::error::Error for Error {
64+
/// Return the first source of an [Exn](crate::Exn) error, or the source of a boxed error.
65+
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
66+
match &self.inner {
67+
Inner::ExnAsError(frame) | Inner::Exn(frame) => frame.children().first().map(exn::Frame::as_error),
68+
}
69+
}
70+
}
71+
72+
impl<E> From<Exn<E>> for Error
73+
where
74+
E: std::error::Error + Send + Sync + 'static,
75+
{
76+
fn from(err: Exn<E>) -> Self {
77+
Error {
78+
inner: Inner::Exn(err.into()),
79+
}
80+
}
81+
}
82+
83+
// TODO: actually use frames, source doesn't really work.
84+
// #[derive(Clone, Copy)]
85+
// pub struct ErrorIter<'a> {
86+
// current: Option<&'a (dyn std::error::Error + 'static)>,
87+
// }
88+
//
89+
// impl<'a> Iterator for ErrorIter<'a> {
90+
// type Item = &'a (dyn std::error::Error + 'static);
91+
//
92+
// fn next(&mut self) -> Option<Self::Item> {
93+
// let current = self.current;
94+
// self.current = self.current.and_then(std::error::Error::source);
95+
// current
96+
// }
97+
// }

gix-error/src/exn/debug.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,16 @@ impl fmt::Debug for Frame {
3232

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

3739
let children = frame.children();
3840
let children_len = children.len();
3941

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

4446
let child_child_len = child.children().len();
4547
if level == 0 && children_len == 1 && child_child_len == 1 {

gix-error/src/exn/display.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,16 @@
1414

1515
use std::fmt;
1616

17-
use crate::exn::Exn;
17+
use crate::exn::{Exn, Frame};
1818

1919
impl<E: std::error::Error + Send + Sync + 'static> fmt::Display for Exn<E> {
2020
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2121
write!(f, "{}", self.as_error())
2222
}
2323
}
24+
25+
impl fmt::Display for Frame {
26+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27+
write!(f, "{}", self.as_error())
28+
}
29+
}

0 commit comments

Comments
 (0)