Skip to content

Commit

Permalink
Change back to std::sync::Mutex; clean up
Browse files Browse the repository at this point in the history
Extract common code to get the inner view
  • Loading branch information
sourcefrog committed Sep 23, 2023
1 parent 8c83bab commit 2cf7788
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 51 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ keywords = ["progress", "progress-bar", "terminal", "ansi"]

[dependencies]
atty = "0.2"
parking_lot = "0.12"
terminal_size = "0.2"
yansi = "0.5"

Expand Down
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Nutmeg Changelog

## Unreleased

- Change back to `std::sync::Mutex` from `parking_lot`, to keep dependencies smaller.

## 0.1.4

Released 2023-09-23
Expand Down
85 changes: 43 additions & 42 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Nutmeg's goal is that [View::update] is cheap enough that applications can call
fairly freely when there are small updates. The library takes care of rate-limiting
updates to the terminal, as configured in the [Options].
Each call to [View::update] will take a `parking_lot` mutex and check the
Each call to [View::update] will take a mutex lock and check the
system time, in addition to running the callback and some function-call overhead.
The model is only rendered to a string, and the string printed to a terminal, if
Expand Down Expand Up @@ -213,11 +213,9 @@ is welcome.

use std::fmt::Display;
use std::io::{self, Write};
use std::sync::Arc;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};

use parking_lot::Mutex;

mod ansi;
mod destination;
mod helpers;
Expand Down Expand Up @@ -407,6 +405,31 @@ impl<M: Model> View<M> {
}
}

/// Call this function on the locked inner view.
///
/// If the view has been destroyed, do nothing.
fn call_inner<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut InnerView<M>) -> R,
{
f(self
.inner
.lock()
.expect("View mutex is not poisoned")
.as_mut()
.expect("View is not already destroyed"))
}

/// Extract the inner view, destroying this object: updates on it will
/// no longer succeed.
fn take_inner(self) -> InnerView<M> {
self.inner
.lock()
.expect("View mutex is not poisoned")
.take()
.expect("View is not already destroyed")
}

/// Stop using this progress view.
///
/// If the progress bar is currently visible, it will be left behind on the
Expand All @@ -416,21 +439,12 @@ impl<M: Model> View<M> {
pub fn abandon(self) -> M {
// Mark it as not drawn (even if it is) so that Drop will not try to
// hide it.
self.inner
.lock()
.take()
.expect("inner state is still present")
.abandon()
.unwrap()
self.take_inner().abandon().expect("Abandoned view")
}

/// Erase the model from the screen (if drawn), destroy it, and return the model.
pub fn finish(self) -> M {
self.inner
.lock()
.take()
.expect("inner state is still present")
.finish()
self.take_inner().finish()
}

/// Update the model, and possibly redraw the screen to reflect the
Expand Down Expand Up @@ -459,25 +473,25 @@ impl<M: Model> View<M> {
where
U: FnOnce(&mut M) -> R,
{
self.inner.lock().as_mut().unwrap().update(update_fn)
self.call_inner(|inner| inner.update(update_fn))
}

/// Hide the progress bar if it's currently drawn, and leave it
/// hidden until [View::resume] is called.
pub fn suspend(&self) {
self.inner.lock().as_mut().unwrap().suspend().unwrap()
self.call_inner(|v| v.suspend().expect("suspend succeeds"))
}

/// Remove the progress bar if it's currently drawn, but allow it
/// to be redrawn when the model is next updated.
pub fn clear(&self) {
self.inner.lock().as_mut().unwrap().clear().unwrap()
self.call_inner(|v| v.clear().expect("clear succeeds"))
}

/// Allow the progress bar to be drawn again, reversing the effect
/// of [View::suspend].
pub fn resume(&self) {
self.inner.lock().as_mut().unwrap().resume().unwrap()
self.call_inner(|v| v.resume().expect("resume succeeds"))
}

/// Set the value of the fake clock, for testing.
Expand All @@ -486,11 +500,7 @@ impl<M: Model> View<M> {
///
/// Moving the clock backwards in time may cause a panic.
pub fn set_fake_clock(&self, fake_clock: Instant) {
self.inner
.lock()
.as_mut()
.unwrap()
.set_fake_clock(fake_clock)
self.call_inner(|v| v.set_fake_clock(fake_clock))
}

/// Inspect the view's model.
Expand All @@ -509,7 +519,7 @@ impl<M: Model> View<M> {
where
F: FnOnce(&mut M) -> R,
{
f(&mut self.inner.lock().as_mut().unwrap().model)
self.call_inner(|v| f(&mut v.model))
}

/// Print a message to the view.
Expand Down Expand Up @@ -543,12 +553,7 @@ impl<M: Model> View<M> {
/// view.message(format!("{} splines reticulated\n", 42));
/// ```
pub fn message<S: AsRef<str>>(&self, message: S) {
self.inner
.lock()
.as_mut()
.unwrap()
.write(message.as_ref().as_bytes())
.expect("writing message");
self.message_bytes(message.as_ref().as_bytes())
}

/// Print a message from a byte buffer.
Expand All @@ -564,12 +569,7 @@ impl<M: Model> View<M> {
/// view.message_bytes(b"hello crow\n");
/// ```
pub fn message_bytes<S: AsRef<[u8]>>(&self, message: S) {
self.inner
.lock()
.as_mut()
.unwrap()
.write(message.as_ref())
.expect("writing message");
self.call_inner(|v| v.write(message.as_ref()).expect("write message"));
}

/// If the view's destination is [Destination::Capture], returns the buffer
Expand All @@ -591,10 +591,10 @@ impl<M: Model> View<M> {
/// let output = view.captured_output();
/// view.message("Captured message\n");
/// drop(view);
/// assert_eq!(output.lock().as_str(), "Captured message\n");
/// assert_eq!(output.lock().unwrap().as_str(), "Captured message\n");
/// ```
pub fn captured_output(&self) -> Arc<Mutex<String>> {
self.inner.lock().as_mut().unwrap().captured_output()
self.call_inner(|v| v.captured_output())
}
}

Expand All @@ -603,7 +603,7 @@ impl<M: Model> io::Write for &View<M> {
if buf.is_empty() {
return Ok(0);
}
self.inner.lock().as_mut().unwrap().write(buf)
self.call_inner(|v| v.write(buf))
}

fn flush(&mut self) -> io::Result<()> {
Expand All @@ -616,7 +616,7 @@ impl<M: Model> io::Write for View<M> {
if buf.is_empty() {
return Ok(0);
}
self.inner.lock().as_mut().unwrap().write(buf)
self.call_inner(|v| v.write(buf))
}

fn flush(&mut self) -> io::Result<()> {
Expand All @@ -629,7 +629,7 @@ impl<M: Model> Drop for View<M> {
// Only try lock here: don't hang if it's locked or panic
// if it's poisoned. And, do nothing if the View has already been
// finished, in which case the contents of the Mutex will be None.
if let Some(mut inner_guard) = self.inner.try_lock() {
if let Ok(mut inner_guard) = self.inner.try_lock() {
if let Some(inner) = Option::take(&mut inner_guard) {
inner.finish();
}
Expand Down Expand Up @@ -858,6 +858,7 @@ impl<M: Model> InnerView<M> {
self.capture_buffer
.get_or_insert_with(|| Arc::new(Mutex::new(String::new())))
.lock()
.expect("lock capture_buffer")
.push_str(buf);
}
}
Expand Down
2 changes: 1 addition & 1 deletion tests/api/identical_output_suppressed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn identical_output_suppressed() {

// No erasure commands, just a newline after the last painted view.
assert_eq!(
output.lock().as_str(),
output.lock().unwrap().as_str(),
"\x1b[?7l\x1b[0Jhundreds=0\x1b[1G\x1b[?7l\x1b[0Jhundreds=1\n"
);
}
17 changes: 10 additions & 7 deletions tests/api/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ fn draw_progress_once() {
drop(view);

assert_eq!(
output.lock().as_str(),
output.lock().unwrap().as_str(),
"\x1b[?7l\x1b[0J count: 1\n bar: *\x1b[1F\x1b[0J\x1b[?7h"
);
}
Expand All @@ -58,7 +58,7 @@ fn abandoned_bar_is_not_erased() {

// No erasure commands, just a newline after the last painted view.
assert_eq!(
output.lock().as_str(),
output.lock().unwrap().as_str(),
"\x1b[?7l\x1b[0J count: 1\n bar: *\n"
);
}
Expand Down Expand Up @@ -96,7 +96,7 @@ fn suspend_and_resume() {
// it's resumed, so 2 is then painted.
// * 3 and 4 are painted in the usual way.
assert_eq!(
output.lock().as_str(),
output.lock().unwrap().as_str(),
"\x1b[?7l\x1b[0JXX: 0\
\x1b[1G\x1b[0J\x1b[?7h\
\x1b[?7l\x1b[0JXX: 2\
Expand All @@ -119,7 +119,7 @@ fn disabled_progress_is_not_drawn() {
}
drop(view);

assert_eq!(output.lock().as_str(), "");
assert_eq!(output.lock().unwrap().as_str(), "");
}

#[test]
Expand All @@ -137,7 +137,10 @@ fn disabled_progress_does_not_block_print() {
}
drop(view);

assert_eq!(output.lock().as_str(), "print line 0\nprint line 1\n");
assert_eq!(
output.lock().unwrap().as_str(),
"print line 0\nprint line 1\n"
);
}

/// If output is redirected, it should not be affected by the width of
Expand All @@ -160,7 +163,7 @@ fn default_width_when_not_on_stdout() {
drop(view);

assert_eq!(
output.lock().as_str(),
output.lock().unwrap().as_str(),
"\x1b[?7l\x1b[0Jwidth=80\x1b[1G\x1b[0J\x1b[?7h"
);
}
Expand Down Expand Up @@ -211,7 +214,7 @@ fn rate_limiting_with_fake_clock() {

drop(view);
assert_eq!(
output.lock().as_str(),
output.lock().unwrap().as_str(),
"\x1b[?7l\x1b[0Jupdate:1 draw:1\
\x1b[1G\
\x1b[?7l\x1b[0Jupdate:11 draw:2\
Expand Down

0 comments on commit 2cf7788

Please sign in to comment.