convey/lib.rs
1//! Easily output stuff for humans and machines alike
2//!
3//! # Examples
4//!
5//! ```rust
6//! extern crate convey;
7//!
8//! fn main() -> Result<(), convey::Error> {
9//! let mut out = convey::new().add_target(convey::human::stdout()?)?;
10//! out.print(convey::components::text("hello world!"))?;
11//! Ok(())
12//! }
13//! ```
14
15#![warn(missing_docs)]
16
17use failure;
18
19/// Create a new output
20pub fn new() -> Output {
21 Output::default()
22}
23
24/// Structure holding your output targets
25#[derive(Default, Clone)]
26pub struct Output {
27 inner: Arc<Mutex<InnerOutput>>,
28}
29
30#[derive(Default, Clone)]
31struct InnerOutput {
32 targets: Vec<Target>,
33 #[cfg(feature = "log")]
34 log_level: Option<log::Level>,
35}
36
37impl Output {
38 /// Add a target to output to
39 pub fn add_target(self, target: Target) -> Result<Self, Error> {
40 {
41 let mut o = self.inner.lock().map_err(|e| Error::sync_error(&e))?;
42 o.targets.push(target);
43 }
44 Ok(self)
45 }
46
47 /// Initializes the global logger with an `Output` instance with
48 /// `max_log_level` set to a specific log level.
49 ///
50 /// ```
51 /// # extern crate convey;
52 /// # fn main() -> Result<(), convey::Error> {
53 /// let output = convey::new()
54 /// .add_target(convey::human::stdout()?)?
55 /// .use_as_logger(log::Level::Debug)?;
56 ///
57 /// log::info!("welcome");
58 /// log::error!("oh noes");
59 /// # Ok(()) }
60 /// ```
61 #[cfg(feature = "log")]
62 pub fn use_as_logger(self, level: log::Level) -> Result<Self, Error> {
63 {
64 let mut o = self.inner.lock().map_err(|e| Error::sync_error(&e))?;
65 o.log_level = Some(level);
66 }
67 log::set_boxed_logger(Box::new(self.clone()))?;
68 log::set_max_level(level.to_level_filter());
69 Ok(self)
70 }
71}
72
73#[test]
74fn assert_output_is_sync_and_send() {
75 fn assert_both<T: Send + Sync>() {}
76 assert_both::<Output>();
77}
78
79use std::sync::{Arc, Mutex};
80
81/// Known targets to write to
82#[derive(Clone)]
83pub struct Target {
84 inner: InnerTarget,
85}
86
87impl Target {
88 /// Human readable output
89 ///
90 /// Will mostly be (unstructured) text, optionally with formatting.
91 pub(crate) fn human(f: human::Formatter) -> Self {
92 Target {
93 inner: InnerTarget::Human(Arc::new(Mutex::new(f))),
94 }
95 }
96
97 /// JSON output
98 ///
99 /// Machines like this.
100 pub(crate) fn json(f: json::Formatter) -> Self {
101 Target {
102 inner: InnerTarget::Json(Arc::new(Mutex::new(f))),
103 }
104 }
105}
106
107#[derive(Clone)]
108enum InnerTarget {
109 Human(Arc<Mutex<human::Formatter>>),
110 Json(Arc<Mutex<json::Formatter>>),
111}
112
113mod error;
114pub use crate::error::Error;
115
116impl Output {
117 /// Print some item to the currently active output targets
118 pub fn print<O: Render>(&self, item: O) -> Result<(), Error> {
119 let mut o = self.inner.lock().map_err(|e| Error::sync_error(&e))?;
120 for target in &mut o.targets {
121 match &target.inner {
122 InnerTarget::Human(fmt) => {
123 let mut fmt = fmt.lock().map_err(|e| Error::sync_error(&e))?;
124 item.render_for_humans(&mut *fmt)?;
125 fmt.write("\n")?;
126 }
127 InnerTarget::Json(fmt) => {
128 let mut fmt = fmt.lock().map_err(|e| Error::sync_error(&e))?;
129 item.render_json(&mut *fmt)?;
130 fmt.write_separator()?;
131 }
132 }
133 }
134
135 Ok(())
136 }
137
138 /// Immediately write all buffered output
139 pub fn flush(&self) -> Result<(), Error> {
140 let o = self.inner.lock().map_err(|e| Error::sync_error(&e))?;
141 for target in &o.targets {
142 match &target.inner {
143 InnerTarget::Human(fmt) => {
144 let fmt = fmt.lock().map_err(|e| Error::sync_error(&e))?;
145 fmt.flush()?;
146 }
147 InnerTarget::Json(fmt) => {
148 let fmt = fmt.lock().map_err(|e| Error::sync_error(&e))?;
149 fmt.flush()?;
150 }
151 }
152 }
153
154 Ok(())
155 }
156}
157
158/// Implement this for your own components
159pub trait Render {
160 /// How to render your type for humans
161 fn render_for_humans(&self, fmt: &mut human::Formatter) -> Result<(), Error>;
162 /// How to render your type to JSON
163 ///
164 /// If your type implements `Serialize`, this can easily just be
165 /// `fmt.write(self)`. Alternatively, you might want to use something like
166 /// serde_json's `json!` macro.
167 fn render_json(&self, fmt: &mut json::Formatter) -> Result<(), Error>;
168}
169
170/// Render automatically works with references
171///
172/// # Examples
173///
174/// ```rust
175/// # extern crate convey;
176/// # use convey::{human, components::text};
177/// # fn main() -> Result<(), convey::Error> {
178/// # let test_target = human::test();
179/// let mut out = convey::new().add_target(test_target.target())?;
180/// out.print(text("owned element"))?;
181/// out.print(&text("reference to an element"))?;
182/// # out.flush()?;
183/// # assert_eq!(test_target.to_string(), "owned element\nreference to an element\n");
184/// # Ok(()) }
185/// ```
186impl<'a, T> Render for &'a T
187where
188 T: Render,
189{
190 fn render_for_humans(&self, fmt: &mut human::Formatter) -> Result<(), Error> {
191 (*self).render_for_humans(fmt)
192 }
193
194 fn render_json(&self, fmt: &mut json::Formatter) -> Result<(), Error> {
195 (*self).render_json(fmt)
196 }
197}
198
199/// Render a string slice
200///
201/// # Examples
202///
203/// ```rust
204/// # extern crate convey;
205/// # use convey::human;
206/// # fn main() -> Result<(), convey::Error> {
207/// # let test_target = human::test();
208/// let mut out = convey::new().add_target(test_target.target())?;
209/// out.print("Hello, World!")?;
210/// # out.flush()?;
211/// # assert_eq!(test_target.to_string(), "Hello, World!\n");
212/// # Ok(()) }
213/// ```
214impl<'a> Render for &'a str {
215 fn render_for_humans(&self, fmt: &mut human::Formatter) -> Result<(), Error> {
216 fmt.write(self.as_bytes())?;
217 Ok(())
218 }
219
220 fn render_json(&self, fmt: &mut json::Formatter) -> Result<(), Error> {
221 fmt.write(&self)?;
222 Ok(())
223 }
224}
225
226/// Render a string
227///
228/// # Examples
229///
230/// ```rust
231/// # extern crate convey;
232/// # use convey::human;
233/// # fn main() -> Result<(), convey::Error> {
234/// # let test_target = human::test();
235/// let mut out = convey::new().add_target(test_target.target())?;
236/// out.print(String::from("Hello, World!"))?;
237/// # out.flush()?;
238/// # assert_eq!(test_target.to_string(), "Hello, World!\n");
239/// # Ok(()) }
240/// ```
241impl<'a> Render for String {
242 fn render_for_humans(&self, fmt: &mut human::Formatter) -> Result<(), Error> {
243 fmt.write(self.as_bytes())?;
244 Ok(())
245 }
246
247 fn render_json(&self, fmt: &mut json::Formatter) -> Result<(), Error> {
248 fmt.write(&self)?;
249 Ok(())
250 }
251}
252
253pub mod components;
254pub mod human;
255pub mod json;
256
257#[cfg(feature = "log")]
258mod logging;
259
260mod test_buffer;