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}