ogle/lib.rs
1// Copyright (C) 2020 Leandro Lisboa Penz <lpenz@lpenz.org>
2// This file is subject to the terms and conditions defined in
3// file 'LICENSE', which is part of this source code package.
4
5#![deny(future_incompatible)]
6#![deny(nonstandard_style)]
7#![deny(missing_docs)]
8#![deny(rustdoc::broken_intra_doc_links)]
9#![allow(rustdoc::private_intra_doc_links)]
10
11//! **ogle** is a program that runs the given command-line periodically,
12//! showing the output only when it is different than the last.
13//!
14//! The simplest way to show most of the features of *ogle* is by asking
15//! it to run `date; sleep` in a shell, with a waiting period of 3s:
16//!
17//! 
18//!
19//! Lines that were written by ogle all start with `=>`. On the first
20//! execution, ogle shows a spinner while the command is running. On the
21//! next executions, ogle shows a progress bar, where the total
22//! corresponds to the duration of the previous execution. The sleep time
23//! is also shown, as a countdown. If the command returns an error to the
24//! shell, the error value is displayed.
25//!
26//! ogle also supports limited interactive control with one-character
27//! commands followed by ENTER:
28//! - `q`: quit after when the process is no longer running.
29//!
30//! # Installation
31//!
32//! If you're a **Rust programmer**, ogle can be installed with `cargo`:
33//!
34//! ```bash
35//! $ cargo install ogle
36//! ```
37//!
38//! If you're a **Debian** user, ogle is available in
39//! [packagecloud](https://packagecloud.io/app/lpenz/debian/search?q=ogle). Follow
40//! these
41//! [instruction](https://packagecloud.io/lpenz/debian/install#manual) to
42//! use the package repository.
43//!
44//!
45//! # Internals
46//!
47//! To make it fully testable, it uses a layered architecture based on
48//! tokio streams which ends up being similar to how we use pipes in a
49//! shell. We can divide it in the following layers:
50//! - wrappers: we have 3 wrapper modules that abtract external
51//! libraries to provide us simpler types or types that provide that
52//! `impl` traits we need. They also make it easier to replate the
53//! underlying implementation in the future, if necessary. Namely:
54//! - [`process_wrapper`]: wraps process instantiation and I/O, and
55//! provides an [`Item`](process_wrapper::Item) that implements
56//! `Eq` so that we can use it in tests.
57//! - [`term_wrapper`]: implements terminal functions, mostly for
58//! output. As we are currently wrapping [`console`] and its
59//! functions require a [`console::Term`] object, we end up using
60//! a mutex here to abstract the singleton.
61//! - [`user_wrapper`]: abstract user interaction. At the moment, we
62//! just monitor `stdin` in line mode, and ogle exits gracefully
63//! when that's detected.
64//! - [`time_wrapper`]: home of the
65//! [`Instant`](time_wrapper::Instant) and
66//! [`Duration`](time_wrapper::Duration) types, which use types
67//! from [`chrono`] at the moment.
68//! - [`sys`]: most of the ogle code doesn't really call functions
69//! that interact with the host system - we have the `sys` module
70//! for that. The module does that by providing a [`sys::SysApi`]
71//! trait that is then implemented by both the [`sys::SysReal`]
72//! type, which calls the system functions; and by the
73//! [`sys::SysVirtual`] type, which can be used to mock these calls
74//! in various ways.
75//!
76//! ```no_compile
77//! sys -> engine -> view -> output
78//! ```
79//!
80//! [watch (1)]: https://linux.die.net/man/1/watch
81//!
82
83use clap::Parser;
84use std::error::Error;
85
86#[macro_use]
87mod misc;
88
89mod cli;
90mod differ;
91mod orchestrator;
92mod progbar;
93
94mod process_wrapper;
95mod term_wrapper;
96mod time_wrapper;
97mod user_wrapper;
98
99mod sys;
100
101mod engine;
102
103mod view;
104
105mod output;
106
107/// Ogle main function, the single pub function in this lib.
108#[tokio::main(flavor = "current_thread")]
109pub async fn main() -> Result<(), Box<dyn Error>> {
110 color_eyre::install()?;
111 tracing_subscriber::fmt()
112 .with_span_events(tracing_subscriber::fmt::format::FmtSpan::ACTIVE)
113 .with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
114 .init();
115 let args = cli::Cli::parse();
116 let sys = sys::SysReal::default();
117 orchestrator::run(args, sys).await?;
118 Ok(())
119}