update docs and readme, bump

This commit is contained in:
askiiart 2025-01-12 18:13:03 -06:00
parent 9108f2d432
commit 2597ea1233
Signed by untrusted user who does not match committer: askiiart
GPG key ID: 6A32977DAF31746A
4 changed files with 71 additions and 59 deletions

2
Cargo.lock generated
View file

@ -4,4 +4,4 @@ version = 4
[[package]]
name = "better-commands"
version = "1.0.0"
version = "1.0.1"

View file

@ -1,7 +1,7 @@
[package]
name = "better-commands"
description = "A better way of running commands - get stdout and stderr together, in order with timestamps, while easily running code as the command runs line-by-line"
version = "1.0.0"
version = "1.0.1"
edition = "2021"
license = "GPL-3.0-only"
keywords = ["command", "cmd"]

View file

@ -1,3 +1,52 @@
# better-commands
# Better Commands
A better way of running commands - get stdout and stderr together, in order with timestamps, while easily running code as the command runs line-by-line.
[![Crates.io Version](https://img.shields.io/crates/v/better-commands)](https://crates.io/crates/better-commands) [![docs.rs](https://img.shields.io/docsrs/better-commands)](https://docs.rs/better-commands/latest/better_commands/)\
This crate provides the ability to more easily run a [`Command`] while also doing something with its output as it runs, as well as providing some extra functionality.
## Why?
Rust's stock [`Command`] kinda sucks, usually it only lets you get the output of a command with stdout and stderr completely separate. This crate lets you run code to handle the output as it prints, as well as providing all the lines together, with timestamps and which stream the line was printed to.
## Features
- Specifies whether a [`Line`] is printed to stderr or stderr
- Provides a timestamp for each [`Line`]
- Provides timestamps for the command as a whole (start, end, and duration)
A basic example (see [`run`]):
```rust
use better_commands::run;
use std::process::Command;
let output = run(Command::new("sleep").arg("1"));
println!("{:?}", output.duration());
```
A more complex example - this lets you provide a function to be run using the output from the command in real-time (see [`run_funcs_with_lines`]):
```rust
use better_commands::run_funcs_with_lines;
use better_commands::Line;
use std::process::Command;
let cmd = run_funcs_with_lines(&mut Command::new("echo").arg("hi"), {
|stdout_lines| { // your function *must* return the lines
let mut lines = Vec::new();
for line in stdout_lines {
lines.push(Line::from_stdout(line.unwrap()));
/* send line to database */
}
return lines;
}
},
{
|stderr_lines| {
// this code is for stderr and won't run because echo won't print anything to stderr, so we'll just put this placeholder here
return Vec::new();
}
});
// prints the following: [Line { printed_to: Stdout, time: Instant { tv_sec: 16316, tv_nsec: 283884648 }, content: "hi" }]
// (timestamp varies)
assert_eq!("hi", cmd.lines().unwrap()[0].content);
```

View file

@ -1,49 +1,4 @@
//! # Better Commands
//!
//! This crate provides the ability to more easily run a [`Command`] while also doing something with its output as it runs, as well as providing some extra functionality:
//!
//! - Specifies whether a [`Line`] is printed to stderr or stderr
//! - Provides a timestamp for each [`Line`]
//! - Provides timestamps for the command as a whole (start, end, and duration)
//!
//! A basic example (see [`run`]):
//!
//! ```
//! use better_commands::run;
//! use std::process::Command;
//!
//! let output = run(Command::new("sleep").arg("1"));
//! println!("{:?}", output.duration());
//! ```
//!
//! A more complex example - this lets you provide a function to be run using the output from the command in real-time (see [`run_funcs_with_lines`]):
//!
//! ```
//! use better_commands::run_funcs_with_lines;
//! use better_commands::Line;
//! use std::process::Command;
//! let cmd = run_funcs_with_lines(&mut Command::new("echo").arg("hi"), {
//! |stdout_lines| { // your function *must* return the lines
//! let mut lines = Vec::new();
//! for line in stdout_lines {
//! lines.push(Line::from_stdout(line.unwrap()));
//! /* send line to database */
//! }
//! return lines;
//! }
//! },
//! {
//! |stderr_lines| {
//! // this code is for stderr and won't run because echo won't print anything to stderr, so we'll just put this placeholder here
//! return Vec::new();
//! }
//! });
//!
//! // prints the following: [Line { printed_to: Stdout, time: Instant { tv_sec: 16316, tv_nsec: 283884648 }, content: "hi" }]
//! // (timestamp varies)
//! assert_eq!("hi", cmd.lines().unwrap()[0].content);
//! ```
#![doc = include_str!("../README.md")]
use std::cmp::Ordering;
use std::io::{BufRead, BufReader, Lines};
use std::process::{ChildStderr, ChildStdout, Command, Stdio};
@ -58,8 +13,6 @@ mod tests;
///
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CmdOutput {
/// The lines printed by the [`Command`]
/// Note: All functions are guaranteed to return either
lines: Option<Vec<Line>>,
status_code: Option<i32>,
start_time: Instant,
@ -68,7 +21,9 @@ pub struct CmdOutput {
}
impl CmdOutput {
/// Returns only stdout
/// Returns only lines printed to stdout
///
/// <small>This is an [`Option`] because [`run_funcs`] cannot provide `lines`</small>
pub fn stdout(self) -> Option<Vec<Line>> {
self.lines.and_then(|lines| {
Some(
@ -80,7 +35,9 @@ impl CmdOutput {
})
}
/// Returns only stdout
/// Returns only lines printed to stderr
///
/// <small>This is an [`Option`] because [`run_funcs`] cannot provide `lines`</small>
pub fn stderr(self) -> Option<Vec<Line>> {
self.lines.and_then(|lines| {
Some(
@ -92,12 +49,17 @@ impl CmdOutput {
})
}
/// Returns all output
/// Returns all lines printed by the [`Command`]\
/// Note: All functions are *guaranteed* to return either `Some()` or `None`, not either
///
/// <small>This is an [`Option`] because [`run_funcs`] cannot provide `lines`</small>
pub fn lines(self) -> Option<Vec<Line>> {
return self.lines;
}
/// Returns the exit status code, if there was one
///
/// Note that if the program exited due to a signal, like SIGKILL, it's possible it didn't exit with a status code, hence this being an [`Option`].
pub fn status_code(self) -> Option<i32> {
return self.status_code;
}
@ -126,12 +88,13 @@ pub enum LineType {
}
/// A single line from the output of a command
///
/// This contains what the line was printed to (stdout/stderr), a timestamp, and the content printed of course.
#[derive(Debug, Clone, PartialEq, Eq, Ord)]
pub struct Line {
/// Which stream the line was printed to
pub printed_to: LineType,
/// When the line was printed
pub time: Instant,
/// The content printed to the line
pub content: String,
}
@ -265,7 +228,7 @@ pub fn run(command: &mut Command) -> CmdOutput {
/// Runs a command while simultaneously running a provided [`Fn`] as the command prints line-by-line
///
/// The [`CmdOutput`] *will* None; this does *not* handle the lines.
/// The [`CmdOutput`] *will* be None; this does *not* handle the lines - if you need them, use [`run`] or [`run_funcs_with_lines`]
///
/// Example:
///
@ -358,7 +321,7 @@ pub fn run_funcs(
/// In order for the built-in `lines` functionality to work, your function must return the lines like this; if this doesn't work for you, you can use [`run`] or [`run_funcs`] instead.
/// ```ignore
/// use better_commands::Line;
///
///
/// let mut lines = Vec::new();
/// for line in stdout_lines {
/// lines.push(Line::from_stdout(line.unwrap())); // from_stdout/from_stderr depending on which