1 /*! Logging library.
2  *
3  * This is probably the only part of squeekboard
4  * that should be doing any direct printing.
5  *
6  * There are several approaches to logging,
7  * in the order of increasing flexibility and/or purity:
8  *
9  * 1. `println!` directly
10  *
11  *   It can't be easily replaced by a different solution
12  *
13  * 2. simple `log!` macro
14  *
15  *   Replacing the destination at runtime other than globally would be awkward,
16  *   so no easy way to suppress errors for things that don't matter,
17  *   but formatting is still easy.
18  *
19  * 3. logging to a mutable destination type
20  *
21  *   Can be easily replaced, but logging `Result` types,
22  *   which should be done by calling a method on the result,
23  *   can't be formatted directly.
24  *   Cannot be parallelized.
25  *
26  * 4. logging to an immutable destination type
27  *
28  *   Same as above, except it can be parallelized.
29  *   Logs being outputs, they get returned
30  *   instead of being misleadingly passed back through arguments.
31  *   It seems more difficult to pass the logger around,
32  *   but this may be a solved problem from the area of functional programming.
33  *
34  * This library generally aims at the approach in 3.
35  * */
36 
37 use std::fmt::Display;
38 
39 /// Levels are not in order.
40 pub enum Level {
41     // Levels for reporting violated constraints
42     /// The program violated a self-imposed constraint,
43     /// ended up in an inconsistent state, and cannot recover.
44     /// Handlers must not actually panic. (should they?)
45     Panic,
46     /// The program violated a self-imposed constraint,
47     /// ended up in an inconsistent state, but some state can be recovered.
48     Bug,
49     /// Invalid data given by an external source,
50     /// some state of the program must be dropped.
51     Error,
52     // Still violated constraints, but harmless
53     /// Invalid data given by an external source, parts of data are ignored.
54     /// No previous program state needs to be dropped.
55     Warning,
56     /// External source not in an expected state,
57     /// but not violating any protocols (including no relevant protocol).
58     Surprise,
59     // Informational
60     /// A change in internal state that results in a change of behaviour
61     /// that a user can observe, and a tinkerer might find useful.
62     /// E.g. selection of external sources, like loading user's UI files,
63     /// language switch, overrides.
64     Info,
65     /// Information useful for application developer only.
66     /// Should be limited to information gotten from external sources,
67     /// and more tricky parts of internal state.
68     Debug,
69 }
70 
71 impl Level {
as_str(&self) -> &'static str72     fn as_str(&self) -> &'static str {
73         match self {
74             Level::Panic => "Panic",
75             Level::Bug => "Bug",
76             Level::Error => "Error",
77             Level::Warning => "Warning",
78             Level::Surprise => "Surprise",
79             Level::Info => "Info",
80             Level::Debug => "Debug",
81         }
82     }
83 }
84 
85 impl From<Problem> for Level {
from(problem: Problem) -> Level86     fn from(problem: Problem) -> Level {
87         use self::Level::*;
88         match problem {
89             Problem::Panic => Panic,
90             Problem::Bug => Bug,
91             Problem::Error => Error,
92             Problem::Warning => Warning,
93             Problem::Surprise => Surprise,
94         }
95     }
96 }
97 
98 /// Only levels which indicate problems
99 /// To use with `Result::Err` handlers,
100 /// which are needed only when something went off the optimal path.
101 /// A separate type ensures that `Err`
102 /// can't end up misclassified as a benign event like `Info`.
103 pub enum Problem {
104     Panic,
105     Bug,
106     Error,
107     Warning,
108     Surprise,
109 }
110 
111 /// Sugar for approach 2
112 // TODO: avoid, deprecate.
113 // Handler instances should be long lived, not one per call.
114 macro_rules! log_print {
115     ($level:expr, $($arg:tt)*) => (::logging::print($level, &format!($($arg)*)))
116 }
117 
118 /// Approach 2
print(level: Level, message: &str)119 pub fn print(level: Level, message: &str) {
120     Print{}.handle(level, message)
121 }
122 
123 /// Sugar for logging errors in results.
124 pub trait Warn where Self: Sized {
125     type Value;
126     /// Approach 2.
or_print(self, level: Problem, message: &str) -> Option<Self::Value>127     fn or_print(self, level: Problem, message: &str) -> Option<Self::Value> {
128         self.or_warn(&mut Print {}, level.into(), message)
129     }
130     /// Approach 3.
or_warn<H: Handler>( self, handler: &mut H, level: Problem, message: &str, ) -> Option<Self::Value>131     fn or_warn<H: Handler>(
132         self,
133         handler: &mut H,
134         level: Problem,
135         message: &str,
136     ) -> Option<Self::Value>;
137 }
138 
139 impl<T, E: Display> Warn for Result<T, E> {
140     type Value = T;
or_warn<H: Handler>( self, handler: &mut H, level: Problem, message: &str, ) -> Option<T>141     fn or_warn<H: Handler>(
142         self,
143         handler: &mut H,
144         level: Problem,
145         message: &str,
146     ) -> Option<T> {
147         self.map_err(|e| {
148             handler.handle(level.into(), &format!("{}: {}", message, e));
149             e
150         }).ok()
151     }
152 }
153 
154 impl<T> Warn for Option<T> {
155     type Value = T;
or_warn<H: Handler>( self, handler: &mut H, level: Problem, message: &str, ) -> Option<T>156     fn or_warn<H: Handler>(
157         self,
158         handler: &mut H,
159         level: Problem,
160         message: &str,
161     ) -> Option<T> {
162         self.or_else(|| {
163             handler.handle(level.into(), message);
164             None
165         })
166     }
167 }
168 
169 /// A mutable handler for text warnings.
170 /// Approach 3.
171 pub trait Handler {
172     /// Handle a log message
handle(&mut self, level: Level, message: &str)173     fn handle(&mut self, level: Level, message: &str);
174 }
175 
176 /// Prints info to stdout, everything else to stderr
177 pub struct Print;
178 
179 impl Handler for Print {
handle(&mut self, level: Level, message: &str)180     fn handle(&mut self, level: Level, message: &str) {
181         match level {
182             Level::Info => println!("Info: {}", message),
183             l => eprintln!("{}: {}", l.as_str(), message),
184         }
185     }
186 }
187 
188 /// Warning handler that will panic
189 /// at any warning, error, surprise, bug, or panic.
190 /// Don't use except in tests
191 pub struct ProblemPanic;
192 
193 impl Handler for ProblemPanic {
handle(&mut self, level: Level, message: &str)194     fn handle(&mut self, level: Level, message: &str) {
195         use self::Level::*;
196         match level {
197             Panic | Bug | Error | Warning | Surprise => panic!("{}", message),
198             l => Print{}.handle(l, message),
199         }
200     }
201 }
202