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;