From 2cf7788d3763371652a02caebce225e79a3abdb9 Mon Sep 17 00:00:00 2001 From: Martin Pool Date: Sat, 23 Sep 2023 11:48:37 -0700 Subject: [PATCH] Change back to std::sync::Mutex; clean up Extract common code to get the inner view --- Cargo.toml | 1 - NEWS.md | 4 ++ src/lib.rs | 85 ++++++++++++------------ tests/api/identical_output_suppressed.rs | 2 +- tests/api/main.rs | 17 +++-- 5 files changed, 58 insertions(+), 51 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a252220..7c4dadd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/NEWS.md b/NEWS.md index 8b17a79..f285354 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 diff --git a/src/lib.rs b/src/lib.rs index 2d0c71c..3ffa7a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 @@ -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; @@ -407,6 +405,31 @@ impl View { } } + /// Call this function on the locked inner view. + /// + /// If the view has been destroyed, do nothing. + fn call_inner(&self, f: F) -> R + where + F: FnOnce(&mut InnerView) -> 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 { + 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 @@ -416,21 +439,12 @@ impl View { 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 @@ -459,25 +473,25 @@ impl View { 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. @@ -486,11 +500,7 @@ impl View { /// /// 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. @@ -509,7 +519,7 @@ impl View { 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. @@ -543,12 +553,7 @@ impl View { /// view.message(format!("{} splines reticulated\n", 42)); /// ``` pub fn message>(&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. @@ -564,12 +569,7 @@ impl View { /// view.message_bytes(b"hello crow\n"); /// ``` pub fn message_bytes>(&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 @@ -591,10 +591,10 @@ impl View { /// 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> { - self.inner.lock().as_mut().unwrap().captured_output() + self.call_inner(|v| v.captured_output()) } } @@ -603,7 +603,7 @@ impl io::Write for &View { 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<()> { @@ -616,7 +616,7 @@ impl io::Write for View { 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<()> { @@ -629,7 +629,7 @@ impl Drop for View { // 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(); } @@ -858,6 +858,7 @@ impl InnerView { self.capture_buffer .get_or_insert_with(|| Arc::new(Mutex::new(String::new()))) .lock() + .expect("lock capture_buffer") .push_str(buf); } } diff --git a/tests/api/identical_output_suppressed.rs b/tests/api/identical_output_suppressed.rs index f43b6e5..9cdf874 100644 --- a/tests/api/identical_output_suppressed.rs +++ b/tests/api/identical_output_suppressed.rs @@ -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" ); } diff --git a/tests/api/main.rs b/tests/api/main.rs index 504d683..fd0a4b3 100644 --- a/tests/api/main.rs +++ b/tests/api/main.rs @@ -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" ); } @@ -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" ); } @@ -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\ @@ -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] @@ -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 @@ -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" ); } @@ -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\