Skip to content

Commit

Permalink
Print Cargo logs with SpinnerBar::println
Browse files Browse the repository at this point in the history
  • Loading branch information
maciektr committed Jun 5, 2024
1 parent 555b958 commit 85f3616
Show file tree
Hide file tree
Showing 12 changed files with 117 additions and 40 deletions.
4 changes: 2 additions & 2 deletions scarb/src/bin/scarb/commands/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ struct CommandsList {
}

impl Message for CommandsList {
fn text(self) -> String {
fn text(&self) -> String {
let mut text = String::from("Installed Commands:\n");
for (name, info) in self.commands {
for (name, info) in self.commands.iter() {
text.push_str(&format!("{:<22}: {}\n", name, info));
}
text
Expand Down
4 changes: 2 additions & 2 deletions scarb/src/bin/scarb/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,14 @@ struct ScriptsList {
}

impl Message for ScriptsList {
fn text(self) -> String {
fn text(&self) -> String {
let mut text = String::new();
write!(text, "Scripts available via `scarb run`",).unwrap();
if !self.single_package {
write!(text, " for package `{}`", self.package).unwrap();
}
writeln!(text, ":",).unwrap();
for (name, definition) in self.scripts {
for (name, definition) in self.scripts.iter() {
writeln!(text, "{:<22}: {}", name, definition).unwrap();
}
text
Expand Down
4 changes: 2 additions & 2 deletions scarb/src/compiler/plugin/proc_macro/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ impl PipedText {
}

impl Message for PipedText {
fn text(self) -> String {
self.0
fn text(&self) -> String {
self.0.to_string()
}

fn structured<S: Serializer>(self, ser: S) -> Result<S::Ok, S::Error> {
Expand Down
21 changes: 3 additions & 18 deletions scarb/tests/build_cairo_plugin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,14 +142,8 @@ fn compile_cairo_plugin() {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
assert!(stdout.contains("Compiling some v1.0.0"));
let lines = stdout.lines().map(ToString::to_string).collect::<Vec<_>>();
let (last, lines) = lines.split_last().unwrap();
assert_matches(r#"[..] Finished release target(s) in [..]"#, last);
let (last, _lines) = lines.split_last().unwrap();
// Line from Cargo output
assert_matches(
r#"[..]Finished `release` profile [optimized] target(s) in[..]"#,
last,
);
assert_matches(r#"[..] Finished release target(s) in [..]"#, last);
}

#[test]
Expand All @@ -171,14 +165,8 @@ fn check_cairo_plugin() {
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
assert!(stdout.contains("Checking some v1.0.0"));
let lines = stdout.lines().map(ToString::to_string).collect::<Vec<_>>();
let (last, lines) = lines.split_last().unwrap();
assert_matches(r#"[..] Finished checking release target(s) in [..]"#, last);
let (last, _lines) = lines.split_last().unwrap();
// Line from Cargo output
assert_matches(
r#"[..]Finished `release` profile [optimized] target(s) in[..]"#,
last,
);
assert_matches(r#"[..] Finished checking release target(s) in [..]"#, last);
}

#[test]
Expand Down Expand Up @@ -225,14 +213,11 @@ fn can_use_json_output() {
r#"{"status":"checking","message":"some v1.0.0 ([..]Scarb.toml)"}"#,
first,
);
let (last, lines) = lines.split_last().unwrap();
let (last, _lines) = lines.split_last().unwrap();
assert_matches(
r#"{"status":"finished","message":"checking release target(s) in [..]"}"#,
last,
);
// Line from Cargo.
let (last, _lines) = lines.split_last().unwrap();
assert_matches(r#"{"reason":"build-finished","success":true}"#, last);
}

#[test]
Expand Down
2 changes: 1 addition & 1 deletion utils/scarb-ui/src/components/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl<T> Message for MachineMessage<T>
where
T: Serialize,
{
fn text(self) -> String {
fn text(&self) -> String {
serde_json::to_string_pretty(&self.0).expect("MachineData must serialize without panics")
}

Expand Down
29 changes: 24 additions & 5 deletions utils/scarb-ui/src/components/spinner.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::sync::{Arc, Weak};
use std::time::Duration;

use indicatif::{ProgressBar, ProgressStyle};

use crate::Widget;
use crate::{PrintOverrider, PrintOverriderState, Widget, WidgetHandle};

/// Spinner widget informing about an ongoing process.
pub struct Spinner {
Expand All @@ -24,7 +25,7 @@ impl Spinner {

/// Finishes the associated [`Spinner`] when dropped.
pub struct SpinnerHandle {
pb: ProgressBar,
pb: Arc<ProgressBar>,
}

impl Drop for SpinnerHandle {
Expand All @@ -33,13 +34,31 @@ impl Drop for SpinnerHandle {
}
}

impl WidgetHandle for SpinnerHandle {
fn print_driver(&self) -> Option<Box<dyn PrintOverrider>> {
struct Overrider(Weak<ProgressBar>);
impl PrintOverrider for Overrider {
fn println(&self, message: &str) -> PrintOverriderState {
let Some(pb) = self.0.upgrade() else {
return PrintOverriderState::Inactive;
};
pb.println(message);
PrintOverriderState::Active
}
}
Some(Box::new(Overrider(Arc::downgrade(&self.pb))))
}
}

impl Widget for Spinner {
type Handle = SpinnerHandle;

fn text(self) -> Self::Handle {
let pb = ProgressBar::new_spinner()
.with_style(Spinner::default_style())
.with_message(self.message);
let pb = Arc::new(
ProgressBar::new_spinner()
.with_style(Spinner::default_style())
.with_message(self.message),
);
pb.enable_steady_tick(Duration::from_millis(120));
SpinnerHandle { pb }
}
Expand Down
2 changes: 1 addition & 1 deletion utils/scarb-ui/src/components/status.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ impl<'a> Status<'a> {
}

impl<'a> Message for Status<'a> {
fn text(self) -> String {
fn text(&self) -> String {
format!(
"{} {}",
Style::from_dotted_str(self.color).bold().apply_to(pad_str(
Expand Down
2 changes: 1 addition & 1 deletion utils/scarb-ui/src/components/typed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl<'a> TypedMessage<'a> {
}

impl<'a> Message for TypedMessage<'a> {
fn text(self) -> String {
fn text(&self) -> String {
if self.skip_type_for_text {
self.message.to_string()
} else {
Expand Down
2 changes: 1 addition & 1 deletion utils/scarb-ui/src/components/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ impl<'a, T> Message for ValueMessage<'a, T>
where
T: Display + Serialize,
{
fn text(self) -> String {
fn text(&self) -> String {
self.value.to_string()
}

Expand Down
55 changes: 53 additions & 2 deletions utils/scarb-ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ pub use indicatif::{
BinaryBytes, DecimalBytes, FormattedDuration, HumanBytes, HumanCount, HumanDuration,
HumanFloatCount,
};
use std::fmt::Debug;
use std::sync::{Arc, RwLock};

pub use message::*;
pub use verbosity::*;
Expand Down Expand Up @@ -53,10 +55,28 @@ pub enum OutputFormat {
/// colour, etc.
///
/// All human-oriented messaging (basically all writes to `stdout`) must go through this object.
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct Ui {
verbosity: Verbosity,
output_format: OutputFormat,
state: Arc<RwLock<State>>,
}

impl Debug for Ui {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ui")
.field("verbosity", &self.verbosity)
.field("output_format", &self.output_format)
.finish()
}
}

/// An encapsulation of the UI state.
///
/// This can be used by `Ui` to store stateful information.
#[derive(Default)]
pub struct State {
active_print_overrider: Option<Box<dyn PrintOverrider + Sync + Send>>,
}

impl Ui {
Expand All @@ -65,6 +85,9 @@ impl Ui {
Self {
verbosity,
output_format,
state: Arc::new(RwLock::new(State {
active_print_overrider: None,
})),
}
}

Expand Down Expand Up @@ -103,6 +126,13 @@ impl Ui {
pub fn widget<T: Widget>(&self, widget: T) -> Option<T::Handle> {
if self.output_format == OutputFormat::Text && self.verbosity >= Verbosity::Normal {
let handle = widget.text();
// Store the handle in state for further interaction.
if let Some(driver) = handle.print_driver() {
self.state
.write()
.expect("failed to acquire write lock")
.active_print_overrider = Some(driver);
}
Some(handle)
} else {
None
Expand Down Expand Up @@ -149,7 +179,28 @@ impl Ui {

fn do_print<T: Message>(&self, message: T) {
match self.output_format {
OutputFormat::Text => message.print_text(),
OutputFormat::Text => {
let read_lock = self.state.read().expect("failed to acquire read lock");
if let Some(widget) = read_lock.active_print_overrider.as_ref() {
match widget.println(message.text().as_str()) {
PrintOverriderState::Active => {
// Print has succeeded.
}
PrintOverriderState::Inactive => {
// The driver is inactive. Print has failed.
message.print_text();
// Clear widget from state.
drop(read_lock);
self.state
.write()
.expect("failed to acquire write lock")
.active_print_overrider = None;
}
}
} else {
message.print_text()
}
}
OutputFormat::Json => message.print_json(),
}
}
Expand Down
8 changes: 4 additions & 4 deletions utils/scarb-ui/src/message.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub trait Message {
/// Return textual representation of this message.
///
/// Default implementation returns empty string, making [`Ui`] skip printing this message.
fn text(self) -> String
fn text(&self) -> String
where
Self: Sized,
{
Expand Down Expand Up @@ -75,13 +75,13 @@ pub trait Message {
}

impl Message for &str {
fn text(self) -> String {
fn text(&self) -> String {
self.to_string()
}
}

impl Message for String {
fn text(self) -> String {
self
fn text(&self) -> String {
self.to_string()
}
}
24 changes: 23 additions & 1 deletion utils/scarb-ui/src/widget.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,30 @@
pub trait Widget {
/// Allows for live interaction with the widget, and its drop is called when the widget should
/// be cleared.
type Handle;
type Handle: WidgetHandle;

/// Display the widget on the standard output, and return a handle for further interaction.
fn text(self) -> Self::Handle;
}

/// A handle for a widget that allows for interaction with it.
pub trait WidgetHandle {
/// Get the print driver for the widget.
fn print_driver(&self) -> Option<Box<dyn PrintOverrider>> {
None
}
}

/// The state of the print overrider handle.
pub enum PrintOverriderState {
/// The print overrider is active and can be used.
Active,
/// The print overrider is no longer active and cannot be used.
Inactive,
}

/// A trait for providing the print functionality of a widget.
pub trait PrintOverrider: Sync + Send {
/// Print the message to the widget controlled output.
fn println(&self, message: &str) -> PrintOverriderState;
}

0 comments on commit 85f3616

Please sign in to comment.