History

Vendor `exn` from https://github.com/fast/exn@bb4d8ea4e4df335c46d4fa3f4f260121f9f84305

This is the basis for substantial changes to make it usable for the `gix-*` crates. The wonderful aspect about this is not only a great reduction in dependencies, but it also removes most proc-macro invocations.

Sebastian Thiel committed 10 months ago

Commit 0eaab701 parent 5ab19a716 files · +846 −0Browse files

Unified · Split

16 files
  1. gix-error/src/exn/Cargo.toml +37 −0
  2. gix-error/src/exn/src/debug.rs +108 −0
  3. gix-error/src/exn/src/display.rs +24 −0
  4. gix-error/src/exn/src/impls.rs +157 −0
  5. gix-error/src/exn/src/lib.rs +124 −0
  6. gix-error/src/exn/src/macros.rs +90 −0
  7. gix-error/src/exn/src/option.rs +45 −0
  8. gix-error/src/exn/src/result.rs +76 −0
  9. gix-error/src/exn/tests/simple.rs +118 −0
  10. gix-error/src/exn/tests/snapshots/simple__bail.snap +5 −0
  11. gix-error/src/exn/tests/snapshots/simple__ensure_fail.snap +5 −0
  12. gix-error/src/exn/tests/snapshots/simple__error_straightforward.snap +13 −0
  13. gix-error/src/exn/tests/snapshots/simple__error_tree.snap +27 −0
  14. gix-error/src/exn/tests/snapshots/simple__from_error.snap +5 −0
  15. gix-error/src/exn/tests/snapshots/simple__option_ext.snap +5 −0
  16. gix-error/src/exn/tests/snapshots/simple__result_ext.snap +7 −0
gix-error/src/exn/Cargo.toml+37−0
@@ -0,0 +1,37 @@
1+# Copyright 2025 FastLabs Developers
2+#
3+# Licensed under the Apache License, Version 2.0 (the "License");
4+# you may not use this file except in compliance with the License.
5+# You may obtain a copy of the License at
6+#
7+# http://www.apache.org/licenses/LICENSE-2.0
8+#
9+# Unless required by applicable law or agreed to in writing, software
10+# distributed under the License is distributed on an "AS IS" BASIS,
11+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+# See the License for the specific language governing permissions and
13+# limitations under the License.
14+
15+[package]
16+name = "exn"
17+version = "0.2.1"
18+
19+description = "A context-aware concrete Error type built on `std::error::Error`."
20+
21+edition.workspace = true
22+homepage.workspace = true
23+license.workspace = true
24+readme.workspace = true
25+repository.workspace = true
26+
27+[package.metadata.docs.rs]
28+all-features = true
29+rustdoc-args = ["--cfg", "docsrs"]
30+
31+[dependencies]
32+
33+[dev-dependencies]
34+insta = { workspace = true }
35+
36+[lints]
37+workspace = true
gix-error/src/exn/src/debug.rs+108−0
@@ -0,0 +1,108 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+use std::fmt;
16+use std::fmt::Formatter;
17+
18+use crate::Error;
19+use crate::Exn;
20+use crate::Frame;
21+
22+impl<E: Error> fmt::Debug for Exn<E> {
23+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
24+ write_exn(f, self.as_frame(), 0, "")
25+ }
26+}
27+
28+impl fmt::Debug for Frame {
29+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
30+ write_exn(f, self, 0, "")
31+ }
32+}
33+
34+fn write_exn(f: &mut Formatter<'_>, frame: &Frame, level: usize, prefix: &str) -> fmt::Result {
35+ write!(f, "{}", frame.as_error())?;
36+ write_location(f, frame)?;
37+
38+ let children = frame.children();
39+ let children_len = children.len();
40+
41+ for (i, child) in children.iter().enumerate() {
42+ write!(f, "\n{}|", prefix)?;
43+ write!(f, "\n{}|-> ", prefix)?;
44+
45+ let child_child_len = child.children().len();
46+ if level == 0 && children_len == 1 && child_child_len == 1 {
47+ write_exn(f, child, 0, prefix)?;
48+ } else if i < children_len - 1 {
49+ write_exn(f, child, level + 1, &format!("{}| ", prefix))?;
50+ } else {
51+ write_exn(f, child, level + 1, &format!("{} ", prefix))?;
52+ }
53+ }
54+
55+ Ok(())
56+}
57+
58+#[cfg(not(windows_test))]
59+fn write_location(f: &mut Formatter<'_>, exn: &Frame) -> fmt::Result {
60+ let location = exn.location();
61+ write!(
62+ f,
63+ ", at {}:{}:{}",
64+ location.file(),
65+ location.line(),
66+ location.column()
67+ )
68+}
69+
70+#[cfg(windows_test)]
71+fn write_location(f: &mut Formatter<'_>, exn: &Frame) -> fmt::Result {
72+ let location = exn.location();
73+ use std::os::windows::ffi::OsStrExt;
74+ use std::path::Component;
75+ use std::path::MAIN_SEPARATOR;
76+ use std::path::Path;
77+
78+ let file = location.file();
79+ let path = Path::new(file);
80+
81+ let mut resolved = String::new();
82+
83+ for c in path.components() {
84+ match c {
85+ Component::RootDir => {}
86+ Component::CurDir => resolved.push('.'),
87+ Component::ParentDir => resolved.push_str(".."),
88+ Component::Prefix(prefix) => {
89+ resolved.push_str(&prefix.as_os_str().to_string_lossy());
90+ continue;
91+ }
92+ Component::Normal(s) => resolved.push_str(&s.to_string_lossy()),
93+ }
94+ resolved.push('/');
95+ }
96+
97+ if path.as_os_str().encode_wide().last() != Some(MAIN_SEPARATOR as u16)
98+ && resolved != "/"
99+ && resolved.ends_with('/')
100+ {
101+ resolved.pop(); // Pop last '/'
102+ }
103+
104+ let line = location.line();
105+ let column = location.column();
106+
107+ write!(f, ", at {resolved}:{line}:{column}")
108+}
gix-error/src/exn/src/display.rs+24−0
@@ -0,0 +1,24 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+use std::fmt;
16+
17+use crate::Error;
18+use crate::Exn;
19+
20+impl<E: Error> fmt::Display for Exn<E> {
21+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22+ write!(f, "{}", self.as_error())
23+ }
24+}
gix-error/src/exn/src/impls.rs+157−0
@@ -0,0 +1,157 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+use std::fmt;
16+use std::marker::PhantomData;
17+use std::panic::Location;
18+
19+use crate::Error;
20+
21+/// An exception type that can hold an error tree and additional context.
22+pub struct Exn<E: Error> {
23+ // trade one more indirection for less stack size
24+ frame: Box<Frame>,
25+ phantom: PhantomData<E>,
26+}
27+
28+impl<E: Error> From<E> for Exn<E> {
29+ #[track_caller]
30+ fn from(error: E) -> Self {
31+ Exn::new(error)
32+ }
33+}
34+
35+impl<E: Error> Exn<E> {
36+ /// Create a new exception with the given error.
37+ ///
38+ /// This will automatically walk the [source chain of the error] and add them as children
39+ /// frames.
40+ ///
41+ /// See also [`Error::raise`] for a fluent way to convert an error into an `Exn` instance.
42+ ///
43+ /// [source chain of the error]: std::error::Error::source
44+ #[track_caller]
45+ pub fn new(error: E) -> Self {
46+ struct SourceError(String);
47+
48+ impl fmt::Debug for SourceError {
49+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50+ fmt::Debug::fmt(&self.0, f)
51+ }
52+ }
53+
54+ impl fmt::Display for SourceError {
55+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56+ fmt::Display::fmt(&self.0, f)
57+ }
58+ }
59+
60+ impl std::error::Error for SourceError {}
61+
62+ fn walk(error: &dyn std::error::Error, location: &'static Location<'static>) -> Vec<Frame> {
63+ if let Some(source) = error.source() {
64+ let children = vec![Frame {
65+ error: Box::new(SourceError(source.to_string())),
66+ location,
67+ children: walk(source, location),
68+ }];
69+ children
70+ } else {
71+ vec![]
72+ }
73+ }
74+
75+ let location = Location::caller();
76+ let children = walk(&error, location);
77+ let frame = Frame {
78+ error: Box::new(error),
79+ location,
80+ children,
81+ };
82+
83+ Self {
84+ frame: Box::new(frame),
85+ phantom: PhantomData,
86+ }
87+ }
88+
89+ /// Create a new exception with the given error and children.
90+ #[track_caller]
91+ pub fn from_iter<T, I>(children: I, err: E) -> Self
92+ where
93+ T: Error,
94+ I: IntoIterator,
95+ I::Item: Into<Exn<T>>,
96+ {
97+ let mut new_exn = Exn::new(err);
98+ for exn in children {
99+ let exn = exn.into();
100+ new_exn.frame.children.push(*exn.frame);
101+ }
102+ new_exn
103+ }
104+
105+ /// Raise a new exception; this will make the current exception a child of the new one.
106+ #[track_caller]
107+ pub fn raise<T: Error>(self, err: T) -> Exn<T> {
108+ let mut new_exn = Exn::new(err);
109+ new_exn.frame.children.push(*self.frame);
110+ new_exn
111+ }
112+
113+ /// Return the current exception.
114+ pub fn as_error(&self) -> &E {
115+ self.frame
116+ .as_any()
117+ .downcast_ref()
118+ .expect("error type must match")
119+ }
120+
121+ /// Return the underlying exception frame.
122+ pub fn as_frame(&self) -> &Frame {
123+ &self.frame
124+ }
125+}
126+
127+/// A frame in the exception tree.
128+pub struct Frame {
129+ /// The error that occurred at this frame.
130+ error: Box<dyn Error>,
131+ /// The source code location where this exception frame was created.
132+ location: &'static Location<'static>,
133+ /// Child exception frames that provide additional context or source errors.
134+ children: Vec<Frame>,
135+}
136+
137+impl Frame {
138+ /// Return the error as a reference to [`std::any::Any`].
139+ pub fn as_any(&self) -> &dyn std::any::Any {
140+ &*self.error
141+ }
142+
143+ /// Return the error as a reference to [`std::error::Error`].
144+ pub fn as_error(&self) -> &dyn std::error::Error {
145+ &*self.error
146+ }
147+
148+ /// Return the source code location where this exception frame was created.
149+ pub fn location(&self) -> &'static Location<'static> {
150+ self.location
151+ }
152+
153+ /// Return a slice of the children of the exception.
154+ pub fn children(&self) -> &[Frame] {
155+ &self.children
156+ }
157+}
gix-error/src/exn/src/lib.rs+124−0
@@ -0,0 +1,124 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+//! A context-aware concrete Error type built on `std::error::Error`
16+//!
17+//! # Examples
18+//!
19+//! ```
20+//! use exn::Result;
21+//! use exn::ResultExt;
22+//! use exn::bail;
23+//!
24+//! // It's recommended to define errors as structs. Exn will maintain the error tree automatically.
25+//! #[derive(Debug)]
26+//! struct LogicError(String);
27+//!
28+//! impl std::fmt::Display for LogicError {
29+//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30+//! write!(f, "logic error: {}", self.0)
31+//! }
32+//! }
33+//!
34+//! impl std::error::Error for LogicError {}
35+//!
36+//! fn do_logic() -> Result<(), LogicError> {
37+//! bail!(LogicError("0 == 1".to_string()));
38+//! }
39+//!
40+//! // Errors can be enum but notably don't need to chain source error.
41+//! #[derive(Debug)]
42+//! enum AppError {
43+//! Fatal { consequences: &'static str },
44+//! Trivial,
45+//! }
46+//!
47+//! impl std::fmt::Display for AppError {
48+//! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49+//! match self {
50+//! AppError::Fatal { consequences } => write!(f, "fatal error: {consequences}"),
51+//! AppError::Trivial => write!(f, "trivial error"),
52+//! }
53+//! }
54+//! }
55+//!
56+//! impl std::error::Error for AppError {}
57+//!
58+//! fn main() {
59+//! if let Err(err) = do_logic().or_raise(|| AppError::Fatal {
60+//! consequences: "math no longer works",
61+//! }) {
62+//! eprintln!("{err:?}");
63+//! }
64+//! }
65+//! ```
66+//!
67+//! The above program will print an error message like:
68+//!
69+//! ```text
70+//! fatal error: math no longer works, at exn/src/lib.rs:44:16
71+//! |
72+//! |-> logic error: 0 == 1, at exn/src/lib.rs:40:5
73+//! ```
74+
75+#![cfg_attr(docsrs, feature(doc_cfg))]
76+#![deny(missing_docs)]
77+
78+mod debug;
79+mod display;
80+mod impls;
81+mod macros;
82+mod option;
83+mod result;
84+
85+pub use self::impls::Exn;
86+pub use self::impls::Frame;
87+pub use self::option::OptionExt;
88+pub use self::result::Result;
89+pub use self::result::ResultExt;
90+
91+/// A trait bound of the supported error type of [`Exn`].
92+pub trait Error: std::error::Error + std::any::Any + Send + Sync + 'static {
93+ /// Raise this error as a new exception.
94+ #[track_caller]
95+ fn raise(self) -> Exn<Self>
96+ where
97+ Self: Sized,
98+ {
99+ Exn::new(self)
100+ }
101+}
102+
103+impl<T> Error for T where T: std::error::Error + std::any::Any + Send + Sync + 'static {}
104+
105+/// Equivalent to `Ok::<_, Exn<E>>(value)`.
106+///
107+/// This simplifies creation of an `exn::Result` in places where type inference cannot deduce the
108+/// `E` type of the result &mdash; without needing to write `Ok::<_, Exn<E>>(value)`.
109+///
110+/// One might think that `exn::Result::Ok(value)` would work in such cases, but it does not.
111+///
112+/// ```console
113+/// error[E0282]: type annotations needed for `std::result::Result<i32, E>`
114+/// --> src/main.rs:11:13
115+/// |
116+/// 11 | let _ = exn::Result::Ok(1);
117+/// | - ^^^^^^^^^^^^^^^ cannot infer type for type parameter `E` declared on the enum `Result`
118+/// | |
119+/// | consider giving this pattern the explicit type `std::result::Result<i32, E>`, where the type parameter `E` is specified
120+/// ```
121+#[expect(non_snake_case)]
122+pub fn Ok<T, E: Error>(value: T) -> Result<T, E> {
123+ Result::Ok(value)
124+}
gix-error/src/exn/src/macros.rs+90−0
@@ -0,0 +1,90 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+/// Creates an [`Exn`] and returns it as [`Result`].
16+///
17+/// Shorthand for `return Err(Exn::from(err))`.
18+///
19+/// # Examples
20+///
21+/// Create an [`Exn`] from [`Error`]:
22+///
23+/// [`Exn`]: crate::Exn
24+/// [`Error`]: std::error::Error
25+///
26+/// ```
27+/// use std::fs;
28+///
29+/// use exn::bail;
30+/// # fn wrapper() -> exn::Result<(), impl exn::Error> {
31+/// match fs::read_to_string("/path/to/file") {
32+/// Ok(content) => println!("file contents: {content}"),
33+/// Err(err) => bail!(err),
34+/// }
35+/// # Ok(()) }
36+/// ```
37+#[macro_export]
38+macro_rules! bail {
39+ ($err:expr) => {{
40+ return ::std::result::Result::Err($crate::Exn::from($err));
41+ }};
42+}
43+
44+/// Ensures `$cond` is met; otherwise return an error.
45+///
46+/// Shorthand for `if !$cond { bail!(...); }`.
47+///
48+/// # Examples
49+///
50+/// Create an [`Exn`] from an [`Error`]:
51+///
52+/// [`Exn`]: crate::Exn
53+/// [`Error`]: std::error::Error
54+///
55+/// ```
56+/// # fn has_permission(_: &u32, _: &u32) -> bool { true }
57+/// # type User = u32;
58+/// # let user = 0;
59+/// # type Resource = u32;
60+/// # let resource = 0;
61+/// use std::error::Error;
62+/// use std::fmt;
63+///
64+/// use exn::ensure;
65+///
66+/// #[derive(Debug)]
67+/// struct PermissionDenied(User, Resource);
68+///
69+/// impl fmt::Display for PermissionDenied {
70+/// fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
71+/// write!(fmt, "permission denied")
72+/// }
73+/// }
74+///
75+/// impl Error for PermissionDenied {}
76+///
77+/// ensure!(
78+/// has_permission(&user, &resource),
79+/// PermissionDenied(user, resource),
80+/// );
81+/// # Ok(())
82+/// ```
83+#[macro_export]
84+macro_rules! ensure {
85+ ($cond:expr, $err:expr $(,)?) => {{
86+ if !bool::from($cond) {
87+ $crate::bail!($err)
88+ }
89+ }};
90+}
gix-error/src/exn/src/option.rs+45−0
@@ -0,0 +1,45 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+use crate::Error;
16+use crate::Exn;
17+use crate::Result;
18+
19+/// An extension trait for [`Option`] to provide raising new exceptions on `None`.
20+pub trait OptionExt {
21+ /// The `Some` type.
22+ type Some;
23+
24+ /// Construct a new [`Exn`] on the `None` variant.
25+ fn ok_or_raise<A, F>(self, err: F) -> Result<Self::Some, A>
26+ where
27+ A: Error,
28+ F: FnOnce() -> A;
29+}
30+
31+impl<T> OptionExt for Option<T> {
32+ type Some = T;
33+
34+ #[track_caller]
35+ fn ok_or_raise<A, F>(self, err: F) -> Result<T, A>
36+ where
37+ A: Error,
38+ F: FnOnce() -> A,
39+ {
40+ match self {
41+ Some(v) => Ok(v),
42+ None => Err(Exn::new(err())),
43+ }
44+ }
45+}
gix-error/src/exn/src/result.rs+76−0
@@ -0,0 +1,76 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+use crate::Error;
16+use crate::Exn;
17+
18+/// A reasonable return type to use throughout an application.
19+pub type Result<T, E> = std::result::Result<T, Exn<E>>;
20+
21+/// An extension trait for [`Result`] to provide context information on [`Exn`]s.
22+pub trait ResultExt {
23+ /// The `Ok` type.
24+ type Success;
25+
26+ /// The `Err` type that would be wrapped in an [`Exn`].
27+ type Error: Error;
28+
29+ /// Raise a new exception on the [`Exn`] inside the [`Result`].
30+ ///
31+ /// Apply [`Exn::raise`] on the `Err` variant, refer to it for more information.
32+ fn or_raise<A, F>(self, err: F) -> Result<Self::Success, A>
33+ where
34+ A: Error,
35+ F: FnOnce() -> A;
36+}
37+
38+impl<T, E> ResultExt for std::result::Result<T, E>
39+where
40+ E: Error,
41+{
42+ type Success = T;
43+ type Error = E;
44+
45+ #[track_caller]
46+ fn or_raise<A, F>(self, err: F) -> Result<Self::Success, A>
47+ where
48+ A: Error,
49+ F: FnOnce() -> A,
50+ {
51+ match self {
52+ Ok(v) => Ok(v),
53+ Err(e) => Err(Exn::new(e).raise(err())),
54+ }
55+ }
56+}
57+
58+impl<T, E> ResultExt for std::result::Result<T, Exn<E>>
59+where
60+ E: Error,
61+{
62+ type Success = T;
63+ type Error = E;
64+
65+ #[track_caller]
66+ fn or_raise<A, F>(self, err: F) -> Result<Self::Success, A>
67+ where
68+ A: Error,
69+ F: FnOnce() -> A,
70+ {
71+ match self {
72+ Ok(v) => Ok(v),
73+ Err(e) => Err(e.raise(err())),
74+ }
75+ }
76+}
gix-error/src/exn/tests/simple.rs+118−0
@@ -0,0 +1,118 @@
1+// Copyright 2025 FastLabs Developers
2+//
3+// Licensed under the Apache License, Version 2.0 (the "License");
4+// you may not use this file except in compliance with the License.
5+// You may obtain a copy of the License at
6+//
7+// http://www.apache.org/licenses/LICENSE-2.0
8+//
9+// Unless required by applicable law or agreed to in writing, software
10+// distributed under the License is distributed on an "AS IS" BASIS,
11+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+// See the License for the specific language governing permissions and
13+// limitations under the License.
14+
15+use exn::Error;
16+use exn::Exn;
17+use exn::OptionExt;
18+use exn::ResultExt;
19+
20+#[derive(Debug)]
21+struct SimpleError(&'static str);
22+
23+impl std::fmt::Display for SimpleError {
24+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25+ write!(f, "{}", self.0)
26+ }
27+}
28+
29+impl std::error::Error for SimpleError {}
30+
31+#[test]
32+fn test_error_straightforward() {
33+ let e1 = SimpleError("E1").raise();
34+ let e2 = e1.raise(SimpleError("E2"));
35+ let e3 = e2.raise(SimpleError("E3"));
36+ let e4 = e3.raise(SimpleError("E4"));
37+ let e5 = e4.raise(SimpleError("E5"));
38+ insta::assert_debug_snapshot!(e5);
39+}
40+
41+#[test]
42+fn test_error_tree() {
43+ let e1 = SimpleError("E1").raise();
44+ let e3 = e1.raise(SimpleError("E3"));
45+
46+ let e9 = SimpleError("E9").raise();
47+ let e10 = e9.raise(SimpleError("E10"));
48+
49+ let e11 = SimpleError("E11").raise();
50+ let e12 = e11.raise(SimpleError("E12"));
51+
52+ let e5 = Exn::from_iter([e3, e10, e12], SimpleError("E5"));
53+
54+ let e2 = SimpleError("E2").raise();
55+ let e4 = e2.raise(SimpleError("E4"));
56+
57+ let e7 = SimpleError("E7").raise();
58+ let e8 = e7.raise(SimpleError("E8"));
59+
60+ let e6 = Exn::from_iter([e5, e4, e8], SimpleError("E6"));
61+ insta::assert_debug_snapshot!(e6);
62+}
63+
64+#[test]
65+fn test_result_ext() {
66+ let result: Result<(), SimpleError> = Err(SimpleError("An error"));
67+ let result = result.or_raise(|| SimpleError("Another error"));
68+ insta::assert_debug_snapshot!(result.unwrap_err());
69+}
70+
71+#[test]
72+fn test_option_ext() {
73+ let result: Option<()> = None;
74+ let result = result.ok_or_raise(|| SimpleError("An error"));
75+ insta::assert_debug_snapshot!(result.unwrap_err());
76+}
77+
78+#[test]
79+fn test_from_error() {
80+ fn foo() -> exn::Result<(), SimpleError> {
81+ Err(SimpleError("An error"))?;
82+ Ok(())
83+ }
84+
85+ let result = foo();
86+ insta::assert_debug_snapshot!(result.unwrap_err());
87+}
88+
89+#[test]
90+fn test_bail() {
91+ fn foo() -> exn::Result<(), SimpleError> {
92+ exn::bail!(SimpleError("An error"));
93+ }
94+
95+ let result = foo();
96+ insta::assert_debug_snapshot!(result.unwrap_err());
97+}
98+
99+#[test]
100+fn test_ensure_ok() {
101+ fn foo() -> exn::Result<(), SimpleError> {
102+ exn::ensure!(true, SimpleError("An error"));
103+ Ok(())
104+ }
105+
106+ foo().unwrap();
107+}
108+
109+#[test]
110+fn test_ensure_fail() {
111+ fn foo() -> exn::Result<(), SimpleError> {
112+ exn::ensure!(false, SimpleError("An error"));
113+ Ok(())
114+ }
115+
116+ let result = foo();
117+ insta::assert_debug_snapshot!(result.unwrap_err());
118+}
gix-error/src/exn/tests/snapshots/simple__bail.snap+5−0
@@ -0,0 +1,5 @@
1+---
2+source: exn/tests/simple.rs
3+expression: result.unwrap_err()
4+---
5+An error, at exn/tests/simple.rs:92:9
gix-error/src/exn/tests/snapshots/simple__ensure_fail.snap+5−0
@@ -0,0 +1,5 @@
1+---
2+source: exn/tests/simple.rs
3+expression: result.unwrap_err()
4+---
5+An error, at exn/tests/simple.rs:112:9
gix-error/src/exn/tests/snapshots/simple__error_straightforward.snap+13−0
@@ -0,0 +1,13 @@
1+---
2+source: exn/tests/simple.rs
3+expression: e5
4+---
5+E5, at exn/tests/simple.rs:37:17
6+|
7+|-> E4, at exn/tests/simple.rs:36:17
8+|
9+|-> E3, at exn/tests/simple.rs:35:17
10+|
11+|-> E2, at exn/tests/simple.rs:34:17
12+|
13+|-> E1, at exn/tests/simple.rs:33:32
gix-error/src/exn/tests/snapshots/simple__error_tree.snap+27−0
@@ -0,0 +1,27 @@
1+---
2+source: exn/tests/simple.rs
3+expression: e6
4+---
5+E6, at exn/tests/simple.rs:60:14
6+|
7+|-> E5, at exn/tests/simple.rs:52:14
8+| |
9+| |-> E3, at exn/tests/simple.rs:44:17
10+| | |
11+| | |-> E1, at exn/tests/simple.rs:43:32
12+| |
13+| |-> E10, at exn/tests/simple.rs:47:18
14+| | |
15+| | |-> E9, at exn/tests/simple.rs:46:32
16+| |
17+| |-> E12, at exn/tests/simple.rs:50:19
18+| |
19+| |-> E11, at exn/tests/simple.rs:49:34
20+|
21+|-> E4, at exn/tests/simple.rs:55:17
22+| |
23+| |-> E2, at exn/tests/simple.rs:54:32
24+|
25+|-> E8, at exn/tests/simple.rs:58:17
26+ |
27+ |-> E7, at exn/tests/simple.rs:57:32
gix-error/src/exn/tests/snapshots/simple__from_error.snap+5−0
@@ -0,0 +1,5 @@
1+---
2+source: exn/tests/simple.rs
3+expression: result.unwrap_err()
4+---
5+An error, at exn/tests/simple.rs:81:9
gix-error/src/exn/tests/snapshots/simple__option_ext.snap+5−0
@@ -0,0 +1,5 @@
1+---
2+source: exn/tests/simple.rs
3+expression: result.unwrap_err()
4+---
5+An error, at exn/tests/simple.rs:74:25
gix-error/src/exn/tests/snapshots/simple__result_ext.snap+7−0
@@ -0,0 +1,7 @@
1+---
2+source: exn/tests/simple.rs
3+expression: result.unwrap_err()
4+---
5+Another error, at exn/tests/simple.rs:67:25
6+|
7+|-> An error, at exn/tests/simple.rs:67:25

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t