trackable/
lib.rs

1//! This crate provides functionalities to
2//! define [trackable](trait.Trackable.html) objects and track those.
3//!
4//! Below is an example that tracks failure of an I/O operation:
5//!
6//! ```no_run
7//! #[macro_use]
8//! extern crate trackable;
9//!
10//! use trackable::error::Failure;
11//!
12//! fn foo() -> Result<(), Failure> {
13//!     track!(std::fs::File::open("/path/to/non_existent_file").map_err(Failure::from_error))?;
14//!     Ok(())
15//! }
16//! fn bar() -> Result<(), Failure> {
17//!     track!(foo())?;
18//!     Ok(())
19//! }
20//! fn baz() -> Result<(), Failure> {
21//!     track!(bar())?;
22//!     Ok(())
23//! }
24//!
25//! fn main() {
26//!     let result = baz();
27//!     assert!(result.is_err());
28//!
29//!     let error = result.err().unwrap();
30//!     assert_eq!(format!("\r{}", error).replace('\\', "/"), r#"
31//! Failed (cause; No such file or directory)
32//! HISTORY:
33//!   [0] at src/lib.rs:7
34//!   [1] at src/lib.rs:12
35//!   [2] at src/lib.rs:16
36//! "#);
37//! }
38//! ```
39//!
40//! This example used the built-in `Failure` type,
41//! but you can easily define your own trackable error types.
42//! See the documentaion of [error](error/index.html) module for more details.
43#![warn(missing_docs)]
44
45#[cfg(feature = "serialize")]
46extern crate serde;
47#[cfg(feature = "serialize")]
48#[macro_use]
49extern crate serde_derive;
50#[macro_use]
51extern crate trackable_derive;
52
53use std::borrow::Cow;
54use std::fmt;
55use std::task::Poll;
56
57#[doc(hidden)]
58pub use trackable_derive::*;
59
60#[macro_use]
61mod macros;
62
63// for `trackable_derive`
64mod trackable {
65    pub use super::*;
66}
67
68pub mod error;
69pub mod result;
70
71/// This trait allows to track an instance of an implementation type.
72///
73/// A trackable instance can have a tracking history that manages own backtrace-like (but more general)
74/// [history](struct.History.html) for tracking.
75///
76/// You can add entries to the history by calling tracking macros(e.g., [track!](macro.track.html)).
77///
78/// See [`TrackableError`](error/struct.TrackableError.html) as a typical implementaion of this trait.
79///
80/// # Examples
81///
82/// Defines a trackable type.
83///
84/// ```
85/// #[macro_use]
86/// extern crate trackable;
87///
88/// use trackable::{Trackable, History, Location};
89///
90/// #[derive(Default)]
91/// struct TrackableObject {
92///     history: History<Location>,
93/// }
94/// impl Trackable for TrackableObject {
95///     type Event = Location;
96///     fn history(&self) -> Option<&History<Self::Event>> {
97///         Some(&self.history)
98///     }
99///     fn history_mut(&mut self) -> Option<&mut History<Self::Event>> {
100///         Some(&mut self.history)
101///     }
102/// }
103///
104/// fn main() {
105///     let o = TrackableObject::default();
106///     let o = track!(o);
107///     let o = track!(o, "Hello");
108///     let o = track!(o, "Hello {}", "World!");
109///
110///     assert_eq!(format!("\n{}", o.history).replace('\\', "/"), r#"
111/// HISTORY:
112///   [0] at src/lib.rs:23
113///   [1] at src/lib.rs:24 -- Hello
114///   [2] at src/lib.rs:25 -- Hello World!
115/// "#);
116/// }
117/// ```
118pub trait Trackable {
119    /// Event type which a history of an instance of this type can have.
120    type Event: From<Location>;
121
122    /// Add an event into the tail of the history of this instance.
123    ///
124    /// Typically, this is called via [track!](macro.track.html) macro.
125    #[inline]
126    fn track<F>(&mut self, f: F)
127    where
128        F: FnOnce() -> Self::Event,
129    {
130        if let Some(h) = self.history_mut() {
131            h.add(f())
132        }
133    }
134
135    /// Returns `true` if it is being tracked, otherwise `false`.
136    #[inline]
137    fn in_tracking(&self) -> bool {
138        self.history().is_some()
139    }
140
141    /// Returns the reference of the tracking history of this instance.
142    ///
143    /// If it is not being tracked, this will return `None.
144    fn history(&self) -> Option<&History<Self::Event>>;
145
146    /// Returns the mutable reference of the tracking history of this instance.
147    ///
148    /// If it is not being tracked, this will return `None.
149    fn history_mut(&mut self) -> Option<&mut History<Self::Event>>;
150}
151impl<T: Trackable> Trackable for Option<T> {
152    type Event = T::Event;
153
154    #[inline]
155    fn history(&self) -> Option<&History<Self::Event>> {
156        self.as_ref().and_then(Trackable::history)
157    }
158
159    #[inline]
160    fn history_mut(&mut self) -> Option<&mut History<Self::Event>> {
161        self.as_mut().and_then(Trackable::history_mut)
162    }
163}
164impl<T, E: Trackable> Trackable for Result<T, E> {
165    type Event = E::Event;
166
167    #[inline]
168    fn history(&self) -> Option<&History<Self::Event>> {
169        self.as_ref().err().and_then(Trackable::history)
170    }
171
172    #[inline]
173    fn history_mut(&mut self) -> Option<&mut History<Self::Event>> {
174        self.as_mut().err().and_then(Trackable::history_mut)
175    }
176}
177
178impl<T: Trackable> Trackable for Poll<T> {
179    type Event = T::Event;
180
181    #[inline]
182    fn history(&self) -> Option<&History<Self::Event>> {
183        if let Poll::Ready(x) = self {
184            x.history()
185        } else {
186            None
187        }
188    }
189
190    #[inline]
191    fn history_mut(&mut self) -> Option<&mut History<Self::Event>> {
192        if let Poll::Ready(x) = self {
193            x.history_mut()
194        } else {
195            None
196        }
197    }
198}
199
200/// The tracking history of a target.
201///
202/// A history is a sequence of the tracked events.
203///
204/// # Examples
205///
206/// ```
207/// use std::fmt::{Display, Formatter, Result};
208/// use trackable::History;
209///
210/// struct Event(&'static str);
211/// impl Display for Event {
212///     fn fmt(&self, f: &mut Formatter) -> Result {
213///         write!(f, "event: {}", self.0)
214///     }
215/// }
216///
217/// let mut history = History::new();
218/// history.add(Event("foo"));
219/// history.add(Event("bar"));
220///
221/// assert_eq!(format!("\n{}", history), r#"
222/// HISTORY:
223///   [0] event: foo
224///   [1] event: bar
225/// "#);
226/// ```
227#[derive(Debug, Clone)]
228#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
229pub struct History<Event>(Vec<Event>);
230impl<Event> History<Event> {
231    /// Makes an empty history.
232    #[inline]
233    pub fn new() -> Self {
234        History(Vec::new())
235    }
236
237    /// Adds an event to the tail of this history.
238    #[inline]
239    pub fn add(&mut self, event: Event) {
240        self.0.push(event);
241    }
242
243    /// Returns the tracked events in this history.
244    #[inline]
245    pub fn events(&self) -> &[Event] {
246        &self.0[..]
247    }
248}
249impl<Event: fmt::Display> fmt::Display for History<Event> {
250    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
251        writeln!(f, "HISTORY:")?;
252        for (i, e) in self.events().iter().enumerate() {
253            writeln!(f, "  [{}] {}", i, e)?;
254        }
255        Ok(())
256    }
257}
258impl<Event> Default for History<Event> {
259    #[inline]
260    fn default() -> Self {
261        History::new()
262    }
263}
264
265/// The location of interest in source code files.
266///
267/// Typically this is created in the macros which defined in this crate.
268#[derive(Debug, Clone)]
269#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
270pub struct Location {
271    module_path: Cow<'static, str>,
272    file: Cow<'static, str>,
273    line: u32,
274    message: Cow<'static, str>,
275}
276impl Location {
277    /// Makes a new `Location` instance.
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// use trackable::Location;
283    ///
284    /// let location = Location::new(module_path!(), file!(), line!(), "Hello".to_string());
285    /// assert_eq!(location.message(), "Hello");
286    /// ```
287    #[inline]
288    pub fn new<M, F, T>(module_path: M, file: F, line: u32, message: T) -> Self
289    where
290        M: Into<Cow<'static, str>>,
291        F: Into<Cow<'static, str>>,
292        T: Into<Cow<'static, str>>,
293    {
294        Location {
295            module_path: module_path.into(),
296            file: file.into(),
297            line,
298            message: message.into(),
299        }
300    }
301
302    /// Gets the crate name of this location.
303    #[inline]
304    pub fn crate_name(&self) -> &str {
305        if let Some(module_path_end) = self.module_path.find(':') {
306            &self.module_path[..module_path_end]
307        } else {
308            self.module_path.as_ref()
309        }
310    }
311
312    /// Gets the module path of this location.
313    #[inline]
314    pub fn module_path(&self) -> &str {
315        self.module_path.as_ref()
316    }
317
318    /// Gets the file name of this location.
319    #[inline]
320    pub fn file(&self) -> &str {
321        self.file.as_ref()
322    }
323
324    /// Gets the line of this location.
325    #[inline]
326    pub fn line(&self) -> u32 {
327        self.line
328    }
329
330    /// Gets the message left at this location.
331    #[inline]
332    pub fn message(&self) -> &str {
333        self.message.as_ref()
334    }
335}
336impl fmt::Display for Location {
337    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
338        write!(f, "at {}:{}", self.file(), self.line())?;
339        if !self.message().is_empty() {
340            write!(f, " -- {}", self.message())?;
341        }
342        Ok(())
343    }
344}
345
346#[cfg(test)]
347mod test {
348    use super::*;
349    use error::Failure;
350
351    #[test]
352    fn it_works() {
353        fn foo() -> Result<(), Failure> {
354            track!(std::fs::File::open("/path/to/non_existent_file")
355                .map_err(|e| Failure::from_error(format!("{:?}", e.kind()))))?;
356            Ok(())
357        }
358        fn bar() -> Result<(), Failure> {
359            track!(foo())?;
360            Ok(())
361        }
362        fn baz() -> Result<(), Failure> {
363            track!(bar())?;
364            Ok(())
365        }
366
367        let result = baz();
368        assert!(result.is_err());
369
370        let error = result.err().unwrap();
371        assert_eq!(
372            format!("\n{}", error).replace('\\', "/"),
373            r#"
374Failed (cause; NotFound)
375HISTORY:
376  [0] at src/lib.rs:354
377  [1] at src/lib.rs:359
378  [2] at src/lib.rs:363
379"#
380        );
381    }
382}