trackable-1.3.0/.cargo_vcs_info.json0000644000000001360000000000100127650ustar { "git": { "sha1": "219733d35b42716ecb6753bcec67043566ad080a" }, "path_in_vcs": "" }trackable-1.3.0/.github/actions-rs/grcov.yml000064400000000000000000000000551046102023000170420ustar 00000000000000ignore-not-existing: true ignore: - "../*" trackable-1.3.0/.github/workflows/ci.yml000064400000000000000000000061011046102023000162660ustar 00000000000000# Based on https://github.com/actions-rs/meta/blob/master/recipes/quickstart.md name: CI on: [push, pull_request] jobs: check: name: Check runs-on: ubuntu-latest strategy: matrix: toolchain: [stable, beta, nightly] steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install ${{ matrix.toolchain }} toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - name: Run cargo check uses: actions-rs/cargo@v1 with: command: check args: --all-features --all test: name: Test Suite runs-on: ubuntu-latest strategy: matrix: toolchain: [stable, beta, nightly] steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install ${{ matrix.toolchain }} toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - name: Run cargo test uses: actions-rs/cargo@v1 with: command: test args: --all-features --all lints: name: Lints runs-on: ubuntu-latest strategy: matrix: toolchain: [stable, beta, nightly] steps: - name: Checkout sources uses: actions/checkout@v2 - name: Install ${{ matrix.toolchain }} toolchain uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.toolchain }} override: true components: rustfmt, clippy - name: Run cargo fmt uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check - name: Run cargo clippy uses: actions-rs/cargo@v1 with: command: clippy args: --all-features --all -- -D warnings grcov: name: Coverage runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install toolchain uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true - name: Execute tests uses: actions-rs/cargo@v1 with: command: test args: --all --all-features env: CARGO_INCREMENTAL: 0 RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort" RUSTDOCFLAGS: "-Cpanic=abort" - name: Gather coverage data id: coverage uses: actions-rs/grcov@v0.1 - name: Coveralls upload uses: coverallsapp/github-action@master with: github-token: ${{ secrets.GITHUB_TOKEN }} parallel: true path-to-lcov: ${{ steps.coverage.outputs.report }} grcov_finalize: runs-on: ubuntu-latest needs: grcov steps: - name: Coveralls finalization uses: coverallsapp/github-action@master with: github-token: ${{ secrets.GITHUB_TOKEN }} parallel-finished: true trackable-1.3.0/.gitignore000064400000000000000000000000221046102023000135370ustar 00000000000000target Cargo.lock trackable-1.3.0/Cargo.lock0000644000000040300000000000100107350ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "proc-macro2" version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" [[package]] name = "serde_derive" version = "1.0.162" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" dependencies = [ "proc-macro2", "quote", "syn 2.0.15", ] [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "trackable" version = "1.3.0" dependencies = [ "serde", "serde_derive", "trackable_derive", ] [[package]] name = "trackable_derive" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebeb235c5847e2f82cfe0f07eb971d1e5f6804b18dac2ae16349cc604380f82f" dependencies = [ "quote", "syn 1.0.109", ] [[package]] name = "unicode-ident" version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" trackable-1.3.0/Cargo.toml0000644000000021520000000000100107630ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "trackable" version = "1.3.0" authors = ["Takeru Ohta "] description = "This library provides a way to track objects manually as an alternative to mechanisms like backtracing" homepage = "https://github.com/sile/trackable" readme = "README.md" license = "MIT" repository = "https://github.com/sile/trackable" [package.metadata.docs.rs] all-features = true [dependencies.serde] version = "1" optional = true [dependencies.serde_derive] version = "1" optional = true [dependencies.trackable_derive] version = "1" [features] serialize = [ "serde", "serde_derive", ] [badges.coveralls] repository = "sile/trackable" trackable-1.3.0/Cargo.toml.orig000064400000000000000000000011661046102023000144500ustar 00000000000000[package] name = "trackable" version = "1.3.0" authors = ["Takeru Ohta "] description = "This library provides a way to track objects manually as an alternative to mechanisms like backtracing" homepage = "https://github.com/sile/trackable" repository = "https://github.com/sile/trackable" readme = "README.md" license = "MIT" [badges] coveralls = {repository = "sile/trackable"} [dependencies] serde = { version = "1", optional = true } serde_derive = { version = "1", optional = true } trackable_derive = "1" [features] serialize = ["serde", "serde_derive"] [package.metadata.docs.rs] all-features = true trackable-1.3.0/LICENSE000064400000000000000000000021051046102023000125600ustar 00000000000000The MIT License Copyright (c) 2017 Takeru Ohta Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. trackable-1.3.0/README.md000064400000000000000000000031341046102023000130350ustar 00000000000000trackable ========= [![Crates.io: trackable](https://img.shields.io/crates/v/trackable.svg)](https://crates.io/crates/trackable) [![Documentation](https://docs.rs/trackable/badge.svg)](https://docs.rs/trackable) [![Actions Status](https://github.com/sile/trackable/workflows/CI/badge.svg)](https://github.com/sile/trackable/actions) [![Coverage Status](https://coveralls.io/repos/github/sile/trackable/badge.svg?branch=master)](https://coveralls.io/github/sile/trackable?branch=master) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) `trackable` provides functionalities to define trackable objects and track those. [Documentation](https://docs.rs/trackable) Below code is an example that tracks failure of an I/O operation: ```rust #[macro_use] extern crate trackable; use trackable::error::Failure; fn foo() -> Result<(), Failure> { track!(std::fs::File::open("/path/to/non_existent_file").map_err(Failure::from_error))?; Ok(()) } fn bar() -> Result<(), Failure> { track!(foo())?; Ok(()) } fn baz() -> Result<(), Failure> { track!(bar())?; Ok(()) } fn main() { let result = baz(); assert!(result.is_err()); let error = result.err().unwrap(); assert_eq!(format!("\r{}", error), r#" Failed (cause; No such file or directory) HISTORY: [0] at rust_out::7 [1] at rust_out::12 [2] at rust_out::16 "#); } ``` This example used the built-in `Failure` type, but you can easily define your own trackable error types. See the documentaion of [error](https://docs.rs/trackable/0.2/trackable/error/index.html) module for more details. trackable-1.3.0/examples/your_own_error_type_template.rs000064400000000000000000000013411046102023000217660ustar 00000000000000#[macro_use] extern crate trackable; use trackable::error::{ErrorKind as TrackableErrorKind, ErrorKindExt}; use trackable::error::{Failure, TrackableError}; #[derive(Debug, Clone, TrackableError)] pub struct Error(TrackableError); impl From for Error { fn from(f: Failure) -> Self { ErrorKind::Other.takes_over(f).into() } } impl From for Error { fn from(f: std::io::Error) -> Self { ErrorKind::Other.cause(f).into() } } #[derive(Debug, Clone)] pub enum ErrorKind { Other, } impl TrackableErrorKind for ErrorKind {} fn main() { let e = ErrorKind::Other.cause("something wrong"); let e = track!(e); let e = track!(e); println!("Error: {}", e); } trackable-1.3.0/src/error.rs000064400000000000000000000350331046102023000140470ustar 00000000000000//! Functionalities for implementing trackable errors and operating on those. //! //! You can easily define your own trackable error types as follows: //! //! ``` //! #[macro_use] //! extern crate trackable; //! use trackable::error::{TrackableError, ErrorKind, ErrorKindExt}; //! //! #[derive(Debug, TrackableError)] //! #[trackable(error_kind = "MyErrorKind")] //! struct MyError(TrackableError); //! impl From for MyError { //! fn from(f: std::io::Error) -> Self { //! // Any I/O errors are considered critical //! MyErrorKind::Critical.cause(f).into() //! } //! } //! //! # #[allow(dead_code)] //! #[derive(Debug, PartialEq, Eq)] //! enum MyErrorKind { //! Critical, //! NonCritical, //! } //! impl ErrorKind for MyErrorKind {} //! //! fn main() { //! // Tracks an error //! let error: MyError = MyErrorKind::Critical.cause("something wrong").into(); //! let error = track!(error); //! let error = track!(error, "I passed here"); //! assert_eq!(format!("\nError: {}", error).replace('\\', "/"), r#" //! Error: Critical (cause; something wrong) //! HISTORY: //! [0] at src/error.rs:27 //! [1] at src/error.rs:28 -- I passed here //! "#); //! //! // Tries to execute I/O operation //! let result = (|| -> Result<_, MyError> { //! let f = track!(std::fs::File::open("/path/to/non_existent_file") //! .map_err(MyError::from))?; //! Ok(f) //! })(); //! let error = result.err().unwrap(); //! let cause = error.concrete_cause::().unwrap(); //! assert_eq!(cause.kind(), std::io::ErrorKind::NotFound); //! } //! ``` //! //! # `TrackableError` drive macro //! //! If it is specified (i.e., `#[derive(TrackableError)]`), //! the following traits will be automatically implemented in the target error type: //! - `Trackable` //! - `Error` //! - `Display` //! - `Deref>` //! - `From<$error_kind>` //! - `From>` //! - `From<$target_error_type> for TrackableError<$error_kind>` //! //! The default value of `$error_kind` is `ErrorKind`. //! It can be customized by using `#[trackable(error_type = "$error_kind")]` attribute. //! //! The target error type must be a newtype (i.e., a tuple struct that has a single element) of `TrackableError`. use std::error::Error; use std::fmt; use std::io; use std::sync::Arc; use super::{Location, Trackable}; /// Boxed `Error` object. pub type BoxError = Box; /// Boxed `ErrorKind` object. pub type BoxErrorKind = Box; /// `History` type specialized for `TrackableError`. pub type History = ::History; /// Built-in `ErrorKind` implementation which represents opaque errors. #[derive(Debug, Default, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Failed; impl ErrorKind for Failed { fn description(&self) -> &str { "Failed" } } /// `TrackableError` type specialized for `Failed`. #[derive(Debug, Clone, TrackableError)] #[trackable(error_kind = "Failed")] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Failure(TrackableError); impl Failure { /// Makes a new `Failure` instance which was caused by the `error`. pub fn from_error(error: E) -> Self where E: Into, { Failed.cause(error).into() } } /// A variant of `std::io::Error` that implements `Trackable` trait. #[derive(Debug, Clone, TrackableError)] #[trackable(error_kind = "io::ErrorKind")] pub struct IoError(TrackableError); impl From for io::Error { fn from(f: IoError) -> Self { io::Error::new(*f.kind(), f) } } impl From for IoError { fn from(f: io::Error) -> Self { f.kind().cause(f).into() } } impl From for IoError { fn from(f: Failure) -> Self { io::ErrorKind::Other.takes_over(f).into() } } impl ErrorKind for io::ErrorKind { fn description(&self) -> &str { "I/O Error" } } /// An `Error` type for unit tests. pub type TestError = TopLevelError; /// An `Error` type for `main` function. pub type MainError = TopLevelError; /// An `Error` type for top-level functions. pub struct TopLevelError(Box); impl From for TopLevelError { fn from(e: E) -> Self { TopLevelError(Box::new(e)) } } impl fmt::Debug for TopLevelError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } impl fmt::Display for TopLevelError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) } } impl Error for TopLevelError { fn source(&self) -> Option<&(dyn Error + 'static)> { Some(&*self.0) } } /// This trait represents an error kind which `TrackableError` can have. pub trait ErrorKind: fmt::Debug { /// A short description of the error kind. /// /// This is used for the description of the error that contains it. /// /// The default implementation always returns `"An error"`. fn description(&self) -> &str { "An error" } /// Displays this kind. /// /// The default implementation uses the debugging form of this. fn display(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:?}", self) } } impl ErrorKind for String { fn description(&self) -> &str { self } } /// An extention of `ErrorKind` trait. /// /// This provides convenient functions to create a `TrackableError` instance of this kind. pub trait ErrorKindExt: ErrorKind + Sized { /// Makes a `TrackableError` instance without cause. /// /// # Examples /// /// ``` /// use std::error::Error; /// use trackable::error::{Failed, ErrorKindExt}; /// /// let e = Failed.error(); /// assert!(e.cause().is_none()); /// ``` #[inline] fn error(self) -> TrackableError { self.into() } /// Makes a `TrackableError` instance with the specified `cause`. /// /// # Examples /// /// ``` /// use std::error::Error; /// use trackable::error::{Failed, ErrorKindExt}; /// /// let e = Failed.cause("something wrong"); /// assert_eq!(e.cause().unwrap().to_string(), "something wrong"); /// ``` #[inline] fn cause(self, cause: E) -> TrackableError where E: Into, { TrackableError::new(self, cause.into()) } /// Takes over from other `TrackableError` instance. /// /// The history of `from` will be preserved. /// /// # Examples /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// # /// use trackable::error::{ErrorKind, ErrorKindExt}; /// /// #[derive(Debug)] /// struct Kind0; /// impl ErrorKind for Kind0 {} /// /// #[derive(Debug)] /// struct Kind1; /// impl ErrorKind for Kind1 {} /// /// fn main() { /// let e = Kind0.error(); /// let e = track!(e); /// /// let e = Kind1.takes_over(e); /// let e = track!(e); /// /// assert_eq!(format!("\nERROR: {}", e).replace('\\', "/"), r#" /// ERROR: Kind1 /// HISTORY: /// [0] at src/error.rs:17 /// [1] at src/error.rs:20 /// "#); /// } /// ``` fn takes_over(self, from: F) -> TrackableError where F: Into>, K: ErrorKind + Send + Sync + 'static, { let from = from.into(); TrackableError { kind: self, cause: from.cause, history: from.history, } } } impl ErrorKindExt for T {} /// Trackable error. /// /// # Examples /// /// Defines your own `Error` type. /// /// ``` /// #[macro_use] /// extern crate trackable; /// use trackable::error::{TrackableError, ErrorKind, ErrorKindExt}; /// /// #[derive(Debug, TrackableError)] /// #[trackable(error_kind = "MyErrorKind")] /// struct MyError(TrackableError); /// impl From for MyError { /// fn from(f: std::io::Error) -> Self { /// // Any I/O errors are considered critical /// MyErrorKind::Critical.cause(f).into() /// } /// } /// /// # #[allow(dead_code)] /// #[derive(Debug, PartialEq, Eq)] /// enum MyErrorKind { /// Critical, /// NonCritical, /// } /// impl ErrorKind for MyErrorKind {} /// /// fn main() { /// // Tracks an error /// let error: MyError = MyErrorKind::Critical.cause("something wrong").into(); /// let error = track!(error); /// let error = track!(error, "I passed here"); /// assert_eq!(format!("\nError: {}", error).replace('\\', "/"), r#" /// Error: Critical (cause; something wrong) /// HISTORY: /// [0] at src/error.rs:27 /// [1] at src/error.rs:28 -- I passed here /// "#); /// /// // Tries to execute I/O operation /// let result = (|| -> Result<_, MyError> { /// let f = track!(std::fs::File::open("/path/to/non_existent_file") /// .map_err(MyError::from))?; /// Ok(f) /// })(); /// let error = result.err().unwrap(); /// let cause = error.concrete_cause::().unwrap(); /// assert_eq!(cause.kind(), std::io::ErrorKind::NotFound); /// } /// ``` /// /// `TrackableError` is cloneable if `K` is so. /// /// ```no_run /// #[macro_use] /// extern crate trackable; /// /// use trackable::Trackable; /// use trackable::error::{Failed, ErrorKindExt}; /// /// fn main() { /// let mut original = Failed.error(); /// /// let original = track!(original, "Hello `original`!"); /// let forked = original.clone(); /// let forked = track!(forked, "Hello `forked`!"); /// /// assert_eq!(format!("\n{}", original).replace('\\', "/"), r#" /// Failed /// HISTORY: /// [0] at src/error.rs:11 -- Hello `original`! /// "#); /// /// assert_eq!(format!("\n{}", forked).replace('\\', "/"), r#" /// Failed /// HISTORY: /// [0] at src/error.rs:11 -- Hello `original`! /// [1] at src/error.rs:13 -- Hello `forked`! /// "#); /// } /// ``` #[derive(Debug, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct TrackableError { kind: K, cause: Option, history: History, } impl TrackableError { /// Makes a new `TrackableError` instance. pub fn new(kind: K, cause: E) -> Self where E: Into, { TrackableError { kind, cause: Some(Cause(Arc::new(cause.into()))), history: History::new(), } } /// Makes a new `TrackableError` instance from `kind`. /// /// Note that the returning error has no cause. fn from_kind(kind: K) -> Self { TrackableError { kind, cause: None, history: History::new(), } } /// Returns the kind of this error. #[inline] pub fn kind(&self) -> &K { &self.kind } /// Tries to return the cause of this error as a value of `T` type. /// /// If neither this error has a cause nor it is an `T` value, /// this method will return `None`. #[inline] pub fn concrete_cause(&self) -> Option<&T> where T: Error + 'static, { self.cause.as_ref().and_then(|c| c.0.downcast_ref()) } } impl From for TrackableError { #[inline] fn from(kind: K) -> Self { Self::from_kind(kind) } } impl Default for TrackableError { #[inline] fn default() -> Self { Self::from_kind(K::default()) } } impl fmt::Display for TrackableError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.kind.display(f)?; if let Some(ref e) = self.cause { write!(f, " (cause; {})", e.0)?; } write!(f, "\n{}", self.history)?; Ok(()) } } impl Error for TrackableError { fn description(&self) -> &str { self.kind.description() } fn cause(&self) -> Option<&dyn Error> { self.cause.as_ref().map::<&dyn Error, _>(|e| &**e.0) } } impl Trackable for TrackableError { type Event = Location; #[inline] fn history(&self) -> Option<&History> { Some(&self.history) } #[inline] fn history_mut(&mut self) -> Option<&mut History> { Some(&mut self.history) } } #[derive(Debug, Clone)] struct Cause(Arc); #[cfg(feature = "serialize")] mod impl_serde { use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::sync::Arc; use super::Cause; impl Serialize for Cause { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&self.0.to_string()) } } impl<'de> Deserialize<'de> for Cause { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; Ok(Cause(Arc::new(s.into()))) } } } #[cfg(test)] mod test { use super::*; use std; #[test] fn it_works() { #[derive(Debug, TrackableError)] #[trackable(error_kind = "MyErrorKind")] struct MyError(TrackableError); impl From for MyError { fn from(f: std::io::Error) -> Self { // Any I/O errors are considered critical MyErrorKind::Critical.cause(f).into() } } #[allow(dead_code)] #[derive(Debug, PartialEq, Eq)] enum MyErrorKind { Critical, NonCritical, } impl ErrorKind for MyErrorKind {} // Tracks an error let error: MyError = MyErrorKind::Critical.cause("something wrong").into(); let error = track!(error); let error = track!(error, "I passed here"); assert_eq!( format!("\nError: {}", error).replace('\\', "/"), r#" Error: Critical (cause; something wrong) HISTORY: [0] at src/error.rs:508 [1] at src/error.rs:509 -- I passed here "# ); // Tries to execute I/O operation let result = (|| -> Result<_, MyError> { let f = track!(std::fs::File::open("/path/to/non_existent_file").map_err(MyError::from,))?; Ok(f) })(); let error = result.err().unwrap(); let cause = error.concrete_cause::().unwrap(); assert_eq!(cause.kind(), std::io::ErrorKind::NotFound); } } trackable-1.3.0/src/lib.rs000064400000000000000000000227251046102023000134700ustar 00000000000000//! This crate provides functionalities to //! define [trackable](trait.Trackable.html) objects and track those. //! //! Below is an example that tracks failure of an I/O operation: //! //! ```no_run //! #[macro_use] //! extern crate trackable; //! //! use trackable::error::Failure; //! //! fn foo() -> Result<(), Failure> { //! track!(std::fs::File::open("/path/to/non_existent_file").map_err(Failure::from_error))?; //! Ok(()) //! } //! fn bar() -> Result<(), Failure> { //! track!(foo())?; //! Ok(()) //! } //! fn baz() -> Result<(), Failure> { //! track!(bar())?; //! Ok(()) //! } //! //! fn main() { //! let result = baz(); //! assert!(result.is_err()); //! //! let error = result.err().unwrap(); //! assert_eq!(format!("\r{}", error).replace('\\', "/"), r#" //! Failed (cause; No such file or directory) //! HISTORY: //! [0] at src/lib.rs:7 //! [1] at src/lib.rs:12 //! [2] at src/lib.rs:16 //! "#); //! } //! ``` //! //! This example used the built-in `Failure` type, //! but you can easily define your own trackable error types. //! See the documentaion of [error](error/index.html) module for more details. #![warn(missing_docs)] #[cfg(feature = "serialize")] extern crate serde; #[cfg(feature = "serialize")] #[macro_use] extern crate serde_derive; #[macro_use] extern crate trackable_derive; use std::borrow::Cow; use std::fmt; use std::task::Poll; #[doc(hidden)] pub use trackable_derive::*; #[macro_use] mod macros; // for `trackable_derive` mod trackable { pub use super::*; } pub mod error; pub mod result; /// This trait allows to track an instance of an implementation type. /// /// A trackable instance can have a tracking history that manages own backtrace-like (but more general) /// [history](struct.History.html) for tracking. /// /// You can add entries to the history by calling tracking macros(e.g., [track!](macro.track.html)). /// /// See [`TrackableError`](error/struct.TrackableError.html) as a typical implementaion of this trait. /// /// # Examples /// /// Defines a trackable type. /// /// ``` /// #[macro_use] /// extern crate trackable; /// /// use trackable::{Trackable, History, Location}; /// /// #[derive(Default)] /// struct TrackableObject { /// history: History, /// } /// impl Trackable for TrackableObject { /// type Event = Location; /// fn history(&self) -> Option<&History> { /// Some(&self.history) /// } /// fn history_mut(&mut self) -> Option<&mut History> { /// Some(&mut self.history) /// } /// } /// /// fn main() { /// let o = TrackableObject::default(); /// let o = track!(o); /// let o = track!(o, "Hello"); /// let o = track!(o, "Hello {}", "World!"); /// /// assert_eq!(format!("\n{}", o.history).replace('\\', "/"), r#" /// HISTORY: /// [0] at src/lib.rs:23 /// [1] at src/lib.rs:24 -- Hello /// [2] at src/lib.rs:25 -- Hello World! /// "#); /// } /// ``` pub trait Trackable { /// Event type which a history of an instance of this type can have. type Event: From; /// Add an event into the tail of the history of this instance. /// /// Typically, this is called via [track!](macro.track.html) macro. #[inline] fn track(&mut self, f: F) where F: FnOnce() -> Self::Event, { if let Some(h) = self.history_mut() { h.add(f()) } } /// Returns `true` if it is being tracked, otherwise `false`. #[inline] fn in_tracking(&self) -> bool { self.history().is_some() } /// Returns the reference of the tracking history of this instance. /// /// If it is not being tracked, this will return `None. fn history(&self) -> Option<&History>; /// Returns the mutable reference of the tracking history of this instance. /// /// If it is not being tracked, this will return `None. fn history_mut(&mut self) -> Option<&mut History>; } impl Trackable for Option { type Event = T::Event; #[inline] fn history(&self) -> Option<&History> { self.as_ref().and_then(Trackable::history) } #[inline] fn history_mut(&mut self) -> Option<&mut History> { self.as_mut().and_then(Trackable::history_mut) } } impl Trackable for Result { type Event = E::Event; #[inline] fn history(&self) -> Option<&History> { self.as_ref().err().and_then(Trackable::history) } #[inline] fn history_mut(&mut self) -> Option<&mut History> { self.as_mut().err().and_then(Trackable::history_mut) } } impl Trackable for Poll { type Event = T::Event; #[inline] fn history(&self) -> Option<&History> { if let Poll::Ready(x) = self { x.history() } else { None } } #[inline] fn history_mut(&mut self) -> Option<&mut History> { if let Poll::Ready(x) = self { x.history_mut() } else { None } } } /// The tracking history of a target. /// /// A history is a sequence of the tracked events. /// /// # Examples /// /// ``` /// use std::fmt::{Display, Formatter, Result}; /// use trackable::History; /// /// struct Event(&'static str); /// impl Display for Event { /// fn fmt(&self, f: &mut Formatter) -> Result { /// write!(f, "event: {}", self.0) /// } /// } /// /// let mut history = History::new(); /// history.add(Event("foo")); /// history.add(Event("bar")); /// /// assert_eq!(format!("\n{}", history), r#" /// HISTORY: /// [0] event: foo /// [1] event: bar /// "#); /// ``` #[derive(Debug, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct History(Vec); impl History { /// Makes an empty history. #[inline] pub fn new() -> Self { History(Vec::new()) } /// Adds an event to the tail of this history. #[inline] pub fn add(&mut self, event: Event) { self.0.push(event); } /// Returns the tracked events in this history. #[inline] pub fn events(&self) -> &[Event] { &self.0[..] } } impl fmt::Display for History { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { writeln!(f, "HISTORY:")?; for (i, e) in self.events().iter().enumerate() { writeln!(f, " [{}] {}", i, e)?; } Ok(()) } } impl Default for History { #[inline] fn default() -> Self { History::new() } } /// The location of interest in source code files. /// /// Typically this is created in the macros which defined in this crate. #[derive(Debug, Clone)] #[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))] pub struct Location { module_path: Cow<'static, str>, file: Cow<'static, str>, line: u32, message: Cow<'static, str>, } impl Location { /// Makes a new `Location` instance. /// /// # Examples /// /// ``` /// use trackable::Location; /// /// let location = Location::new(module_path!(), file!(), line!(), "Hello".to_string()); /// assert_eq!(location.message(), "Hello"); /// ``` #[inline] pub fn new(module_path: M, file: F, line: u32, message: T) -> Self where M: Into>, F: Into>, T: Into>, { Location { module_path: module_path.into(), file: file.into(), line, message: message.into(), } } /// Gets the crate name of this location. #[inline] pub fn crate_name(&self) -> &str { if let Some(module_path_end) = self.module_path.find(':') { &self.module_path[..module_path_end] } else { self.module_path.as_ref() } } /// Gets the module path of this location. #[inline] pub fn module_path(&self) -> &str { self.module_path.as_ref() } /// Gets the file name of this location. #[inline] pub fn file(&self) -> &str { self.file.as_ref() } /// Gets the line of this location. #[inline] pub fn line(&self) -> u32 { self.line } /// Gets the message left at this location. #[inline] pub fn message(&self) -> &str { self.message.as_ref() } } impl fmt::Display for Location { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "at {}:{}", self.file(), self.line())?; if !self.message().is_empty() { write!(f, " -- {}", self.message())?; } Ok(()) } } #[cfg(test)] mod test { use super::*; use error::Failure; #[test] fn it_works() { fn foo() -> Result<(), Failure> { track!(std::fs::File::open("/path/to/non_existent_file") .map_err(|e| Failure::from_error(format!("{:?}", e.kind()))))?; Ok(()) } fn bar() -> Result<(), Failure> { track!(foo())?; Ok(()) } fn baz() -> Result<(), Failure> { track!(bar())?; Ok(()) } let result = baz(); assert!(result.is_err()); let error = result.err().unwrap(); assert_eq!( format!("\n{}", error).replace('\\', "/"), r#" Failed (cause; NotFound) HISTORY: [0] at src/lib.rs:354 [1] at src/lib.rs:359 [2] at src/lib.rs:363 "# ); } } trackable-1.3.0/src/macros.rs000064400000000000000000000446551046102023000142140ustar 00000000000000/// Tries to track the current [location](struct.Location.html) into the history of the `$target`. /// /// `$target` must be evaluated to a value which implements [Trackable](trait.Trackable.html) trait. /// /// If `$target.in_tracking()` is `false`, it will simply return the value of `$target` untouched. /// /// # Examples /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// # /// # fn main() { /// use trackable::error::{Failed, ErrorKindExt}; /// /// // Makes a `TrackableError` value /// let e = Failed.cause("something wrong"); /// let e = track!(e); /// /// // `Result<_, TrackableError>` implements `Trackable` /// let message = "This is a note about this location"; /// let e: Result<(), _> = Err(e); /// let e = track!(e; message); /// /// // `Option` implements `Trackable` /// let e = Some(e); /// let e = track!(e, "Hello {}", "World!"); /// /// assert_eq!(format!("\n{}", e.unwrap().err().unwrap()).replace('\\', "/"), r#" /// Failed (cause; something wrong) /// HISTORY: /// [0] at src/macros.rs:10 /// [1] at src/macros.rs:15 -- message="This is a note about this location" /// [2] at src/macros.rs:19 -- Hello World! /// "#); /// # } /// ``` #[macro_export] macro_rules! track { ($target:expr) => { { use $crate::Trackable; let mut target = $target; target.track(|| { let location = $crate::Location::new( module_path!(), file!(), line!(), String::new()); From::from(location) }); target } }; ($target:expr; $($value:expr),+) => { $crate::track!($target, $crate::trackable_prepare_values_fmt!($($value),+), $($value),+) }; ($target:expr, $message:expr) => { { use $crate::Trackable; let mut target = $target; target.track(|| { let location = $crate::Location::new(module_path!(), file!(), line!(), $message); From::from(location) }); target } }; ($target:expr, $message:expr; $($value:expr),+) => { $crate::track!($target, concat!($message, "; ", $crate::trackable_prepare_values_fmt!($($value),+)), $($value),+) }; ($target:expr, $($format_arg:tt)+) => { { $crate::track!($target, format!($($format_arg)+)) } }; } /// The abbreviation of `track!($target.map_err(Failure::from_error), ..)`. /// /// # Examples /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// # /// # fn main() { /// use std::sync::mpsc; /// use trackable::error::{Failed, ErrorKindExt}; /// /// let rx = mpsc::channel::<()>().1; /// let result = track_any_err!(rx.recv(), "sender dropped"); /// /// assert_eq!(format!("\n{}", result.err().unwrap()).replace('\\', "/"), r#" /// Failed (cause; receiving on a closed channel) /// HISTORY: /// [0] at src/macros.rs:10 -- sender dropped /// "#); /// # } /// ``` #[macro_export] macro_rules! track_any_err { ($target:expr) => { $target.map_err(|e| $crate::track!($crate::error::Failure::from_error(e))) }; ($target:expr; $($arg:tt)*) => { $target.map_err(|e| $crate::track!($crate::error::Failure::from_error(e); $($arg)*)) }; ($target:expr, $($arg:tt)*) => { $target.map_err(|e| $crate::track!($crate::error::Failure::from_error(e), $($arg)*)) }; } /// The abbreviation of `$target.map_err(|e| track!(e, ..))`. #[macro_export] macro_rules! track_err { ($target:expr) => { $target.map_err(|e| $crate::track!(e)) }; ($target:expr; $($arg:tt)*) => { $target.map_err(|e| $crate::track!(e; $($arg)*)) }; ($target:expr, $($arg:tt)*) => { $target.map_err(|e| $crate::track!(e, $($arg)*)) }; } /// Error trackable variant of the standard `assert!` macro. /// /// This is a simple wrapper of the `track_panic!` macro. /// It will call `track_panic!($error_kind, $($format_arg)+)` if `$cond` is evaluated to `false`. /// /// # Examples /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// # /// # fn main() { /// use trackable::error::{Failed, Failure}; /// /// fn add_positive_f32(a: f32, b: f32) -> Result { /// track_assert!(a > 0.0 && b > 0.0, Failed; a, b); /// Ok(a + b) /// } /// /// let r = add_positive_f32(3.0, 2.0); // Ok /// assert_eq!(r.ok(), Some(5.0)); /// /// let r = add_positive_f32(1.0, -2.0); // Err /// assert!(r.is_err()); /// assert_eq!(format!("\n{}", r.err().unwrap()).replace('\\', "/"), r#" /// Failed (cause; assertion failed: `a > 0.0 && b > 0.0`; a=1.0, b=-2.0) /// HISTORY: /// [0] at src/macros.rs:9 /// "#); /// # } /// ``` #[macro_export] macro_rules! track_assert { ($cond:expr, $error_kind:expr) => { if ! $cond { $crate::track_panic!($error_kind, "assertion failed: `{}`", stringify!($cond)) } }; ($cond:expr, $error_kind:expr; $($value:expr),+) => { $crate::track_assert!($cond, $error_kind, $crate::trackable_prepare_values_fmt!($($value),+), $($value),+) }; ($cond:expr, $error_kind:expr, $message:expr) => { $crate::track_assert!($cond, $error_kind, $message,) }; ($cond:expr, $error_kind:expr, $message:expr; $($value:expr),+) => { $crate::track_assert!($cond, $error_kind, concat!($message, "; ", $crate::trackable_prepare_values_fmt!($($value),+)), $($value),+) }; ($cond:expr, $error_kind:expr, $fmt:expr, $($arg:tt)*) => { if ! $cond { $crate::track_panic!($error_kind, concat!("assertion failed: `{}`; ", $fmt), stringify!($cond), $($arg)*) } }; } /// Error trackable variant of the standard `assert_ne!` macro. /// /// Conceptually, `track_assert_eq!(left, right, error_kind)` is equivalent to /// `track_assert!(left == right, error_kind)`. #[macro_export] macro_rules! track_assert_eq { ($left:expr, $right:expr, $error_kind:expr) => { { let left = &$left; let right = &$right; $crate::track_assert!(left == right, $error_kind, "assertion failed: `(left == right)` (left: `{:?}`, right: `{:?}`)", left, right) } }; ($left:expr, $right:expr, $error_kind:expr; $($value:expr),+) => { $crate::track_assert_eq!($left, $right, $error_kind, $crate::trackable_prepare_values_fmt!($($value),+), $($value),+) }; ($left:expr, $right:expr, $error_kind:expr, $message:expr) => { $crate::track_assert_eq!($left, $right, $error_kind, $message,) }; ($left:expr, $right:expr, $error_kind:expr, $message:expr; $($value:expr),+) => { $crate::track_assert_eq!($left, $right, $error_kind, concat!($message, "; ", $crate::trackable_prepare_values_fmt!($($value),+)), $($value),+) }; ($left:expr, $right:expr, $error_kind:expr, $fmt:expr, $($arg:tt)*) => { { let left = &$left; let right = &$right; $crate::track_assert!( left == right, $error_kind, concat!("assertion failed: `(left == right)` (left: `{:?}`, right: `{:?}`): ", $fmt), left, right, $($arg)*) } }; } /// Error trackable variant of the standard `assert_ne!` macro. /// /// Conceptually, `track_assert_ne!(left, right, error_kind)` is equivalent to /// `track_assert!(left != right, error_kind)`. #[macro_export] macro_rules! track_assert_ne { ($left:expr, $right:expr, $error_kind:expr) => { { let left = &$left; let right = &$right; $crate::track_assert!(left != right, $error_kind, "assertion failed: `(left != right)` (left: `{:?}`, right: `{:?}`)", left, right) } }; ($left:expr, $right:expr, $error_kind:expr; $($value:expr),+) => { $crate::track_assert_ne!($left, $right, $error_kind, $crate::trackable_prepare_values_fmt!($($value),+), $($value),+) }; ($left:expr, $right:expr, $error_kind:expr, $message:expr) => { $crate::track_assert_ne!($left, $right, $error_kind, $message,) }; ($left:expr, $right:expr, $error_kind:expr, $message:expr; $($value:expr),+) => { $crate::track_assert_ne!($left, $right, $error_kind, concat!($message, "; ", $crate::trackable_prepare_values_fmt!($($value),+)), $($value),+) }; ($left:expr, $right:expr, $error_kind:expr, $fmt:expr, $($arg:tt)*) => { { let left = &$left; let right = &$right; $crate::track_assert!( left != right, $error_kind, concat!("assertion failed: `(left != right)` (left: `{:?}`, right: `{:?}`): ", $fmt), left, right, $($arg)*) } }; } /// Trackable assertion for `Option` values. /// /// This is a simple wrapper of the `track_panic!` macro. /// It will call `track_panic!` if `$expr` is evaluated to `None`. /// /// # Examples /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// # /// # fn main() { /// use trackable::error::{Failed, Failure}; /// /// fn trackable_checked_sub(a: u32, b: u32) -> Result { /// let n = track_assert_some!(a.checked_sub(b), Failed); /// Ok(n) /// } /// /// let r = trackable_checked_sub(10, 2); // Ok /// assert_eq!(r.ok(), Some(8)); /// /// let r = trackable_checked_sub(2, 10); // Err /// assert!(r.is_err()); /// assert_eq!(format!("\n{}", r.err().unwrap()).replace('\\', "/"), r#" /// Failed (cause; assertion failed: `a.checked_sub(b).is_some()`) /// HISTORY: /// [0] at src/macros.rs:9 /// "#); /// # } /// ``` #[macro_export] macro_rules! track_assert_some { ($expr:expr, $error_kind:expr) => { if let Some(v) = $expr { v } else { $crate::track_panic!($error_kind, "assertion failed: `{}.is_some()`", stringify!($expr)) } }; ($expr:expr, $error_kind:expr; $($value:expr),+) => { $crate::track_assert_some!($expr, $error_kind, $crate::trackable_prepare_values_fmt!($($value),+), $($value),+) }; ($expr:expr, $error_kind:expr, $message:expr) => { $crate::track_assert_some!($expr, $error_kind, $message,) }; ($expr:expr, $error_kind:expr, $message:expr; $($value:expr),+) => { $crate::track_assert_some!($expr, $error_kind, concat!($message, "; ", $crate::trackable_prepare_values_fmt!($($value),+)), $($value),+) }; ($expr:expr, $error_kind:expr, $fmt:expr, $($arg:tt)*) => { if let Some(v) = $expr { v } else { $crate::track_panic!($error_kind, concat!("assertion failed: `{}.is_some()`; ", $fmt), stringify!($expr), $($arg)*) } }; } /// Error trackable variant of the standard `panic!` macro. /// /// This returns an `TrackableError` object as the result value of the calling function, /// instead of aborting the current thread. /// /// Conceptually, `track_panic!(error)` is equivalent to the following code: /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// # /// # use trackable::error::{Failed, Failure}; /// # fn main() { let _ = foo(); } /// # fn foo() -> Result<(), Failure> { /// use trackable::Trackable; /// use trackable::error::TrackableError; /// /// # let error = Failed; /// let e = TrackableError::from(error); // Converts to `TrackableError` /// let e = track!(e); // Tracks this location /// Err(e)?; // Returns from the current function /// # Ok(()) /// # } /// ``` /// /// # Examples /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// # /// # fn main() { /// use trackable::error::{Failed, Failure}; /// /// fn foo(f: F) -> Result<(), Failure> where F: FnOnce() -> Result<(), Failure> { f() } /// /// let e = foo(|| track_panic!(Failed) ).err().unwrap(); /// assert_eq!(format!("\n{}", e).replace('\\', "/"), r#" /// Failed /// HISTORY: /// [0] at src/macros.rs:10 /// "#); /// /// let e = foo(|| track_panic!(Failed, "something {}", "wrong") ).err().unwrap(); /// assert_eq!(format!("\n{}", e).replace('\\', "/"), r#" /// Failed (cause; something wrong) /// HISTORY: /// [0] at src/macros.rs:17 /// "#); /// # } /// ``` #[macro_export] macro_rules! track_panic { ($error:expr) => { { let e = $crate::error::TrackableError::from($error); let e = $crate::track!(e); return Err(From::from(e)); } }; ($error:expr; $($value:expr),+) => { $crate::track_panic!($error, $crate::trackable_prepare_values_fmt!($($value),+), $($value),+) }; ($error_kind:expr, $message:expr) => { { use $crate::error::ErrorKindExt; $crate::track_panic!($error_kind.cause($message)) } }; ($error:expr, $message:expr; $($value:expr),+) => { $crate::track_panic!($error, concat!($message, "; ", $crate::trackable_prepare_values_fmt!($($value),+)), $($value),+) }; ($error_kind:expr, $($format_arg:tt)+) => { { $crate::track_panic!($error_kind, format!($($format_arg)+)) } }; } /// More human readable variant of the standard `Result::unwrap` method. /// /// # Examples /// /// ```no_run /// #[macro_use] /// extern crate trackable; /// /// use trackable::error::{Failed, Failure, ErrorKindExt}; /// /// fn main() { /// let result: Result<(), Failure> = Err(Failed.error().into()); /// /// // Following two expressions are conceptually equivalent. /// result.clone().unwrap(); /// track_try_unwrap!(result.clone()); /// /// // `track_try_unwrap!()` can take additional arguments compatible to `format!()`. /// result.clone().expect(&format!("Additional information: {}", "foo")); /// track_try_unwrap!(result.clone(), "Additional information: {}", "foo"); /// } /// ``` #[macro_export] macro_rules! track_try_unwrap { ($expr:expr) => { match $crate::track!($expr) { Err(e) => { panic!("\nEXPRESSION: {}\nERROR: {}\n", stringify!($expr), e) } Ok(v) => { v } } }; ($expr:expr, $($format_arg:tt)*) => { match $crate::track!($expr, $($format_arg)*) { Err(e) => { panic!("\nEXPRESSION: {}\nERROR: {}\n", stringify!($expr), e) } Ok(v) => { v } } }; } /// Implements the typical traits for a newtype $error of `TrackableError<$kind>`. /// /// The automatically implemented traits are `Deref`, `From`, `Display`, `Error`, /// `Trackable` and `From`. /// /// This macro is useful to reduce the boilerplate code when /// you define a your own trackable error type. /// /// # Examples /// /// ``` /// # #[macro_use] /// # extern crate trackable; /// use trackable::error::{TrackableError, ErrorKind as TrackableErrorKind}; /// /// #[derive(Debug, Clone, PartialEq, Eq)] /// pub enum ErrorKind { /// Foo, /// Bar, /// Baz, /// } /// impl TrackableErrorKind for ErrorKind {} /// /// // Defines a newtype of `TrackableError`. /// #[derive(Debug, Clone)] /// pub struct Error(TrackableError); /// derive_traits_for_trackable_error_newtype!(Error, ErrorKind); /// /// # fn main() {} /// ``` #[deprecated( since = "0.2.19", note = "please use `#[derive(TrackableError)]` instead" )] #[macro_export] macro_rules! derive_traits_for_trackable_error_newtype { ($error:ident, $kind:ty) => { impl ::std::ops::Deref for $error { type Target = $crate::error::TrackableError<$kind>; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl ::std::fmt::Display for $error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { self.0.fmt(f) } } impl ::std::error::Error for $error { fn source(&self) -> Option<&(::std::error::Error + 'static)> { self.0.source() } } impl $crate::Trackable for $error { type Event = $crate::Location; #[inline] fn history(&self) -> Option<&$crate::History> { self.0.history() } #[inline] fn history_mut(&mut self) -> Option<&mut $crate::History> { self.0.history_mut() } } impl From<$crate::error::TrackableError<$kind>> for $error { #[inline] fn from(f: $crate::error::TrackableError<$kind>) -> Self { $error(f) } } impl From<$error> for $crate::error::TrackableError<$kind> { #[inline] fn from(f: $error) -> Self { f.0 } } impl From<$kind> for $error { #[inline] fn from(f: $kind) -> Self { use $crate::error::ErrorKindExt; f.error().into() } } }; } #[doc(hidden)] #[macro_export] macro_rules! trackable_prepare_values_fmt { () => {}; ($value:expr) => { concat!(stringify!($value), "={:?}") }; ($value:expr, $($rest:expr),*) => { concat!(stringify!($value), "={:?}, ", $crate::trackable_prepare_values_fmt!($($rest),*)) }; } #[cfg(test)] mod test { use error::{ErrorKindExt, Failed, Failure}; #[test] fn track_works() { fn foo(bar: Result<(), Failure>) -> Result<(), Failure> { struct Baz { qux: usize, } let baz = Baz { qux: 0 }; track!(bar.clone())?; track!(bar.clone(), "hello")?; track!(bar.clone(), "baz.qux={}", baz.qux)?; Ok(()) } assert!(foo(Ok(())).is_ok()); } #[test] fn track_assert_works() { fn add_positive_f32(a: f32, b: f32) -> Result { track_assert!(a > 0.0 && b > 0.0, Failed); Ok(a + b) } let r = add_positive_f32(3.0, 2.0); // Ok assert_eq!(r.ok(), Some(5.0)); let r = add_positive_f32(1.0, -2.0); // Err assert!(r.is_err()); assert_eq!( format!("\n{}", r.err().unwrap()).replace('\\', "/"), r#" Failed (cause; assertion failed: `a > 0.0 && b > 0.0`) HISTORY: [0] at src/macros.rs:564 "# ); } #[test] #[should_panic] fn track_try_unwrap_works() { track_try_unwrap!(Err(Failed.error())); } } trackable-1.3.0/src/result.rs000064400000000000000000000014161046102023000142320ustar 00000000000000//! Trackable [`Result`] types for main and test functions. //! //! [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html use error::TopLevelError; /// A variant of [`Result`] for top-level functions that return a trackable error on failure. /// /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html pub type TopLevelResult = Result<(), TopLevelError>; /// A variant of [`Result`] for main functions that return a trackable error on failure. /// /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html pub type MainResult = TopLevelResult; /// A variant of [`Result`] for unit tests that return a trackable error on failure. /// /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html pub type TestResult = TopLevelResult;