#![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;
#[doc(hidden)]
pub use trackable_derive::*;
#[macro_use]
mod macros;
mod trackable {
pub use super::*;
}
pub mod error;
pub mod result;
pub trait Trackable {
type Event: From<Location>;
#[inline]
fn track<F>(&mut self, f: F)
where
F: FnOnce() -> Self::Event,
{
if let Some(h) = self.history_mut() {
h.add(f())
}
}
#[inline]
fn in_tracking(&self) -> bool {
self.history().is_some()
}
fn history(&self) -> Option<&History<Self::Event>>;
fn history_mut(&mut self) -> Option<&mut History<Self::Event>>;
}
impl<T: Trackable> Trackable for Option<T> {
type Event = T::Event;
#[inline]
fn history(&self) -> Option<&History<Self::Event>> {
self.as_ref().and_then(Trackable::history)
}
#[inline]
fn history_mut(&mut self) -> Option<&mut History<Self::Event>> {
self.as_mut().and_then(Trackable::history_mut)
}
}
impl<T, E: Trackable> Trackable for Result<T, E> {
type Event = E::Event;
#[inline]
fn history(&self) -> Option<&History<Self::Event>> {
self.as_ref().err().and_then(Trackable::history)
}
#[inline]
fn history_mut(&mut self) -> Option<&mut History<Self::Event>> {
self.as_mut().err().and_then(Trackable::history_mut)
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct History<Event>(Vec<Event>);
impl<Event> History<Event> {
#[inline]
pub fn new() -> Self {
History(Vec::new())
}
#[inline]
pub fn add(&mut self, event: Event) {
self.0.push(event);
}
#[inline]
pub fn events(&self) -> &[Event] {
&self.0[..]
}
}
impl<Event: fmt::Display> fmt::Display for History<Event> {
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<Event> Default for History<Event> {
#[inline]
fn default() -> Self {
History::new()
}
}
#[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 {
#[inline]
pub fn new<M, F, T>(module_path: M, file: F, line: u32, message: T) -> Self
where
M: Into<Cow<'static, str>>,
F: Into<Cow<'static, str>>,
T: Into<Cow<'static, str>>,
{
Location {
module_path: module_path.into(),
file: file.into(),
line,
message: message.into(),
}
}
#[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()
}
}
#[inline]
pub fn module_path(&self) -> &str {
self.module_path.as_ref()
}
#[inline]
pub fn file(&self) -> &str {
self.file.as_ref()
}
#[inline]
pub fn line(&self) -> u32 {
self.line
}
#[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:331
[1] at src/lib.rs:336
[2] at src/lib.rs:340
"#
);
}
}