use crate::io::SendWriter;
use crate::reporter::event::SubcommandResult;
use crate::reporter::Message;
use crate::Event;
use std::io;
use std::io::{Stderr, Stdout};
#[cfg(test)]
use std::sync::MutexGuard;
use std::sync::{Arc, Mutex};
use storyteller::EventHandler;
#[derive(Debug)]
pub struct MinimalOutputHandler<S: SendWriter, F: SendWriter> {
success_writer: Arc<Mutex<S>>,
failure_writer: Arc<Mutex<F>>, }
#[cfg(test)]
impl<S: SendWriter, F: SendWriter> MinimalOutputHandler<S, F> {
fn new(success_writer: S, failure_writer: F) -> Self {
Self {
success_writer: Arc::new(Mutex::new(success_writer)),
failure_writer: Arc::new(Mutex::new(failure_writer)),
}
}
}
impl MinimalOutputHandler<Stdout, Stderr> {
pub fn stderr() -> Self {
Self {
success_writer: Arc::new(Mutex::new(io::stdout())),
failure_writer: Arc::new(Mutex::new(io::stderr())),
}
}
}
#[cfg(test)]
impl<S: SendWriter, F: SendWriter> MinimalOutputHandler<S, F> {
fn inner_success_writer(&self) -> MutexGuard<'_, S> {
self.success_writer
.lock()
.expect("Unable to lock success_writer")
}
fn inner_failure_writer(&self) -> MutexGuard<'_, F> {
self.failure_writer
.lock()
.expect("Unable to lock failure_writer")
}
}
impl<S: SendWriter, F: SendWriter> EventHandler for MinimalOutputHandler<S, F> {
type Event = Event;
fn handle(&self, event: Self::Event) {
macro_rules! success_writeln {
($($arg:tt)*) => {{
let mut writer = self.success_writer.lock().expect("Unable to lock success_writer");
let _ = writeln!(&mut writer, $($arg)*);
}};
}
macro_rules! failure_writeln {
($($arg:tt)*) => {{
let mut writer = self.failure_writer.lock().expect("Unable to lock failure_writer");
let _ = writeln!(&mut writer, $($arg)*);
}};
}
if let Message::SubcommandResult(result) = event.message() {
match result {
SubcommandResult::Find(inner) => match inner.msrv() {
Some(v) => {
success_writeln!("{}", v)
}
None => failure_writeln!("{}", "none"),
},
SubcommandResult::List(_inner) => {
failure_writeln!("unsupported")
}
SubcommandResult::Set(inner) => {
success_writeln!("{}", inner.version())
}
SubcommandResult::Show(inner) => {
success_writeln!("{}", inner.version())
}
SubcommandResult::Verify(inner) if inner.is_compatible() => {
success_writeln!("true")
}
SubcommandResult::Verify(_inner) => {
failure_writeln!("false")
}
}
}
}
}
#[cfg(test)]
mod tests {
use crate::context::list::ListMsrvVariant;
use crate::context::SearchMethod;
use crate::dependency_graph::DependencyGraph;
use crate::manifest::bare_version::BareVersion;
use crate::reporter::event::{
FindResult, ListResult, Progress, SetResult, ShowResult, VerifyResult,
};
use crate::reporter::ui::minimal::MinimalOutputHandler;
use crate::rust::Toolchain;
use crate::semver;
use camino::Utf8Path;
use cargo_metadata::PackageId;
use storyteller::EventHandler;
#[test]
fn find_with_result() {
let msrv = semver::Version::new(1, 10, 100);
let min_available = BareVersion::ThreeComponents(1, 0, 0);
let max_available = BareVersion::ThreeComponents(2, 0, 0);
let event = FindResult::new_msrv(
msrv,
"x",
min_available,
max_available,
SearchMethod::Linear,
);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "1.10.100\n");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "");
}
#[test]
fn find_without_result() {
let min_available = BareVersion::ThreeComponents(1, 0, 0);
let max_available = BareVersion::ThreeComponents(2, 0, 0);
let event = FindResult::none("x", min_available, max_available, SearchMethod::Linear);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "none\n");
}
#[test]
fn list_direct_deps() {
let package_id = PackageId {
repr: "hello_world".to_string(),
};
let dep_graph = DependencyGraph::empty(package_id);
let event = ListResult::new(ListMsrvVariant::DirectDeps, dep_graph);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "unsupported\n");
}
#[test]
fn set_output() {
let event = SetResult::new(
BareVersion::TwoComponents(1, 20),
Utf8Path::new("/my/path").to_path_buf(),
);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "1.20\n");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "");
}
#[test]
fn show_output() {
let event = ShowResult::new(
BareVersion::ThreeComponents(1, 40, 3),
Utf8Path::new("/my/path").to_path_buf(),
);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "1.40.3\n");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "");
}
#[test]
fn verify_true() {
let event = VerifyResult::compatible(Toolchain::new(
semver::Version::new(1, 2, 3),
"test_target",
&[],
));
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "true\n");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "");
}
#[test]
fn verify_false_no_error_message() {
let event = VerifyResult::incompatible(
Toolchain::new(semver::Version::new(1, 2, 3), "test_target", &[]),
None,
);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "false\n");
}
#[test]
fn verify_false_with_error_message() {
let event = VerifyResult::incompatible(
Toolchain::new(semver::Version::new(1, 2, 3), "test_target", &[]),
Some("error message".to_string()),
);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "false\n");
}
#[test]
fn unreported_event() {
let event = Progress::new(1, 100, 50);
let s = Vec::new();
let f = Vec::new();
let handler = MinimalOutputHandler::new(s, f);
handler.handle(event.into());
let s = handler.inner_success_writer().clone();
let s = String::from_utf8_lossy(&s);
assert_eq!(s.as_ref(), "");
let f = handler.inner_failure_writer().clone();
let f = String::from_utf8_lossy(&f);
assert_eq!(f.as_ref(), "");
}
}