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//! ![demo](https://raw.githubusercontent.com/lpenz/ogle/main/demos/demo-sleep.gif)
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}