Skip to content

Commit

Permalink
release: 0.13.0
Browse files Browse the repository at this point in the history
  • Loading branch information
joshstoik1 committed Feb 16, 2024
2 parents 26dfe22 + f08a279 commit f21affc
Show file tree
Hide file tree
Showing 23 changed files with 238 additions and 68 deletions.
8 changes: 4 additions & 4 deletions CREDITS.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# Project Dependencies
Package: fyi
Version: 0.12.0
Generated: 2024-02-08 18:56:22 UTC
Version: 0.13.0
Generated: 2024-02-16 02:31:51 UTC

| Package | Version | Author(s) | License |
| ---- | ---- | ---- | ---- |
| [argyle](https://github.com/Blobfolio/argyle) | 0.7.1 | [Blobfolio, LLC.](mailto:[email protected]) | WTFPL |
| [argyle](https://github.com/Blobfolio/argyle) | 0.7.2 | [Blobfolio, LLC.](mailto:[email protected]) | WTFPL |
| [const_fn](https://github.com/taiki-e/const_fn) | 0.4.9 | | Apache-2.0 or MIT |
| [dactyl](https://github.com/Blobfolio/dactyl) | 0.7.0 | [Blobfolio, LLC.](mailto:[email protected]) | WTFPL |
| [fyi_msg](https://github.com/Blobfolio/fyi) | 0.12.0 | [Blobfolio, LLC.](mailto:[email protected]) | WTFPL |
| [fyi_msg](https://github.com/Blobfolio/fyi) | 0.13.0 | [Blobfolio, LLC.](mailto:[email protected]) | WTFPL |
| [tz-rs](https://github.com/x-hgg-x/tz-rs) | 0.6.14 | x-hgg-x | Apache-2.0 or MIT |
| [utc2k](https://github.com/Blobfolio/utc2k) | 0.8.0 | [Blobfolio, LLC.](mailto:[email protected]) | WTFPL |
4 changes: 2 additions & 2 deletions fyi/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
[package]
name = "fyi"
version = "0.12.0"
version = "0.13.0"
license = "WTFPL"
authors = ["Blobfolio, LLC. <[email protected]>"]
edition = "2021"
rust-version = "1.70"
rust-version = "1.72"
description = "A dead-simple CLI status message printer for use in BASH scripts, etc."
repository = "https://github.com/Blobfolio/fyi"
publish = false
Expand Down
4 changes: 2 additions & 2 deletions fyi_msg/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
[package]
name = "fyi_msg"
version = "0.12.0"
version = "0.13.0"
authors = ["Blobfolio, LLC. <[email protected]>"]
edition = "2021"
rust-version = "1.70"
rust-version = "1.72"
description = "Simple ANSI-formatted, prefixed messages for console printing."
license = "WTFPL"
repository = "https://github.com/Blobfolio/fyi"
Expand Down
2 changes: 1 addition & 1 deletion fyi_msg/benches/fm_msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ benches!(
.run(|| Msg::new(MsgKind::Error, TEXT)),

Bench::new("fyi_msg::MsgKind::into_msg()")
.run(|| MsgKind::Error.into_msg(TEXT))
.run(|| MsgKind::Error.into_msg(TEXT)),
);
12 changes: 12 additions & 0 deletions fyi_msg/examples/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,18 @@ fn main() {

println!();

Msg::info("The formatting can be stripped too.")
.without_ansi()
.print();

#[cfg(feature = "timestamps")]
Msg::info("So pale! Ew!")
.with_timestamp(true)
.without_ansi()
.print();

println!();

// A prompt to STDERR.
if Msg::new(MsgKind::Confirm, "Did this print to STDERR?").eprompt_with_default(true) {
Msg::plain("Great!")
Expand Down
2 changes: 1 addition & 1 deletion fyi_msg/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ For more usage examples, check out the `examples/msg` demo, which covers just ab
*/

#![deny(unsafe_code)]
#![forbid(unsafe_code)]

#![warn(
clippy::filetype_is_file,
Expand Down
45 changes: 23 additions & 22 deletions fyi_msg/src/msg/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,35 +168,19 @@ impl<const N: usize> MsgBuffer<N> {
///
/// This method returns the underlying vector as a string slice.
///
/// ## Panics
///
/// This method will panic if the contents are not valid UTF-8.
pub fn as_str(&self) -> &str { std::str::from_utf8(&self.buf).unwrap() }

#[allow(unsafe_code)]
#[must_use]
#[inline]
/// # As Str (Unchecked).
///
/// This method returns the underlying vector as a string slice.
///
/// ## Safety
///
/// The string must be valid UTF-8 or undefined things will happen.
pub unsafe fn as_str_unchecked(&self) -> &str {
std::str::from_utf8_unchecked(&self.buf)
}
/// If the buffer contains invalid UTF-8, an empty string slice will be
/// returned instead.
pub fn as_str(&self) -> &str { std::str::from_utf8(&self.buf).unwrap_or_default() }

#[must_use]
#[inline]
/// # Into String.
///
/// Consume and return the underlying vector as a `String`.
///
/// ## Panics
///
/// This method will panic if the contents are not valid UTF-8.
pub fn into_string(self) -> String { String::from_utf8(self.buf).unwrap() }
/// If the buffer contains invalid UTF-8, an empty string will be returned
/// instead.
pub fn into_string(self) -> String { String::from_utf8(self.buf).unwrap_or_default() }

#[allow(clippy::missing_const_for_fn)] // Doesn't work.
#[must_use]
Expand Down Expand Up @@ -396,6 +380,23 @@ impl<const N: usize> MsgBuffer<N> {
mod tests {
use super::*;

#[test]
fn as_str() {
let mut buf = MsgBuffer::<BUFFER3>::from_raw_parts(
vec![b'h', b'e', b'l', b'l', b'o'],
[
0, 5,
5, 5,
5, 5,
]
);
assert_eq!(buf.as_str(), "hello");

// Invalid UTF-8.
buf.replace(0, b"Hello \xF0\x90\x80World");
assert_eq!(buf.as_str(), "");
}

#[test]
fn extend() {
let mut buf = MsgBuffer::<BUFFER3>::from_raw_parts(
Expand Down
91 changes: 85 additions & 6 deletions fyi_msg/src/msg/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,30 @@ impl Msg {
self.set_suffix(suffix);
self
}

#[must_use]
/// # Without ANSI Formatting.
///
/// Strip any ANSI formatting from the message.
///
/// Note that subsequent changes might re-introduce ANSI formatting, so
/// this should generally be the last operation before display.
///
/// For unchained usage, see [`Msg::strip_ansi`].
///
/// ## Examples
///
/// ```no_run
/// use fyi_msg::Msg;
///
/// Msg::info("5,000 matching files were found.")
/// .without_ansi()
/// .print();
/// ```
pub fn without_ansi(mut self) -> Self {
self.strip_ansi();
self
}
}

/// ## Setters.
Expand Down Expand Up @@ -739,6 +763,44 @@ impl Msg {
where S: AsRef<str> {
self.0.replace(PART_SUFFIX, suffix.as_ref().as_bytes());
}

/// # Strip ANSI Formatting.
///
/// Remove colors, bold, etc. from the message.
///
/// Note that subsequent changes might re-introduce ANSI formatting, so
/// this should generally be the last operation before display.
///
/// See also [`Msg::without_ansi`].
///
/// Returns true if the content was modified.
pub fn strip_ansi(&mut self) -> bool {
// Iterate through all the parts (except indent and newline), replacing
// the content as needed.
let mut changed = false;
for i in 1..=PART_SUFFIX {
let old = self.0.get(i);
if old.contains(&b'\x1b') {
let mut new = old.to_vec();
let mut in_ansi = false;
new.retain(|&b|
if in_ansi {
if matches!(b, b'm' | b'A' | b'K') { in_ansi = false; }
false
}
else if b == b'\x1b' {
in_ansi = true;
false
}
else { true }
);
self.0.replace(i, &new);
changed = true;
}
}

changed
}
}

#[cfg(feature = "progress")]
Expand Down Expand Up @@ -791,20 +853,16 @@ impl Msg {
/// # As Bytes.
///
/// Return the entire message as a byte slice. Alternatively, you could
/// dereference the struct or use [`Msg::as_ref`] or [`Msg::borrow`].
/// dereference the struct or use [`Msg::as_ref`].
pub fn as_bytes(&self) -> &[u8] { &self.0 }

#[allow(unsafe_code)]
#[must_use]
#[inline]
/// # As Str.
///
/// Return the entire message as a string slice. Alternatively, you could
/// use [`Msg::as_ref`] or [`Msg::borrow`].
pub fn as_str(&self) -> &str {
debug_assert!(std::str::from_utf8(&self.0).is_ok(), "Bug: Message is not UTF8.");
unsafe { std::str::from_utf8_unchecked(&self.0) }
}
pub fn as_str(&self) -> &str { self.0.as_str() }

#[must_use]
#[inline]
Expand Down Expand Up @@ -1110,6 +1168,27 @@ mod tests {
assert!(msg.ends_with(b"My dear aunt"));
}

#[test]
fn t_strip_ansi() {
let mut msg = Msg::info("Hello \x1b[1mWorld!\x1b[0m")
.with_suffix(" \x1b[2m(foo)\x1b[0m");

// Strip it.
assert!(msg.strip_ansi());
assert_eq!(msg.as_str(), "Info: Hello World! (foo)\n");

// Already stripped!
assert!(! msg.strip_ansi());

// Test that without comes out the same.
assert_eq!(
msg,
Msg::info("Hello \x1b[1mWorld!\x1b[0m")
.with_suffix(" \x1b[2m(foo)\x1b[0m")
.without_ansi(),
);
}

#[cfg(feature = "fitted")]
#[test]
fn t_fitted() {
Expand Down
82 changes: 80 additions & 2 deletions fyi_msg/src/progress/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use super::TASK_PREFIX;
pub(super) struct ProglessTask {
task: Box<[u8]>,
width: u16,
ansi: bool,
}

impl Borrow<[u8]> for ProglessTask {
Expand Down Expand Up @@ -61,9 +62,11 @@ impl ProglessTask {
let src = src.as_ref();
if src.is_empty() { None }
else if let Ok(width) = u16::try_from(fitted::width(src)) {
let ansi = src.contains(&b'\x1b');
Some(Self {
task: Box::from(src),
width,
ansi,
})
}
else { None }
Expand All @@ -79,14 +82,89 @@ impl ProglessTask {
let end = fitted::length_width(&self.task, usize::from(avail));
if end > 0 {
buf.extend_from_slice(TASK_PREFIX);
buf.extend_from_slice(&self.task[..end]);
if self.ansi {
// Only push non-ANSI characters.
buf.extend(NoAnsi::new(self.task.iter().take(end).copied()));
}
else { buf.extend_from_slice(&self.task[..end]); }
buf.push(b'\n');
}
}
else {
buf.extend_from_slice(TASK_PREFIX);
buf.extend_from_slice(&self.task);
if self.ansi {
// Only push non-ANSI characters.
buf.extend(NoAnsi::new(self.task.iter().copied()));
}
else { buf.extend_from_slice(&self.task); }
buf.push(b'\n');
}
}
}



/// # No ANSI Iterator.
///
/// This wrapper omits any ANSI formatting blocks within a byte slice while
/// returning everything else.
struct NoAnsi<I: Iterator<Item=u8>> {
inner: I,
in_ansi: bool,
}

impl<I: Iterator<Item=u8>> Iterator for NoAnsi<I> {
type Item = u8;

fn next(&mut self) -> Option<Self::Item> {
loop {
let next = self.inner.next()?;
if self.in_ansi {
if matches!(next, b'm' | b'A' | b'K') { self.in_ansi = false; }
}
else if next == b'\x1b' { self.in_ansi = true; }
else {
return Some(next);
}
}
}

fn size_hint(&self) -> (usize, Option<usize>) {
let (_, max) = self.inner.size_hint();
(0, max)
}
}

impl<I: Iterator<Item=u8>> NoAnsi<I> {
#[inline]
/// # New.
const fn new(inner: I) -> Self { Self { inner, in_ansi: false } }
}



#[cfg(test)]
mod tests {
use super::*;

#[test]
fn t_no_ansi() {
// No ansi should come through unchanged.
assert_eq!(
NoAnsi::new(b"hello".iter().copied()).collect::<Vec<u8>>(),
b"hello",
);

// Basic ansi should get stripped.
assert_eq!(
NoAnsi::new(b"\x1b[1;30mhello\x1b[0m".iter().copied()).collect::<Vec<u8>>(),
b"hello",
);

// All ansi should strip everything.
assert_eq!(
NoAnsi::new(b"\x1b[1m".iter().copied()).collect::<Vec<u8>>(),
Vec::<u8>::new(),
);
}
}
4 changes: 2 additions & 2 deletions release/man/fyi-blank.1
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.TH "FYI BLANK" "1" "February 2024" "blank v0.12.0" "User Commands"
.TH "FYI BLANK" "1" "February 2024" "blank v0.13.0" "User Commands"
.SH NAME
blank \- Manual page for blank v0.12.0.
blank \- Manual page for blank v0.13.0.
.SH DESCRIPTION
Print blank line(s).
.SS USAGE:
Expand Down
Loading

0 comments on commit f21affc

Please sign in to comment.