use storyteller::{event_channel, ChannelEventListener, ChannelReporter, EventListener};
use crate::reporter::event::ScopeCounter;
use crate::TResult;
pub use ui::DiscardOutputHandler;
pub use ui::HumanProgressHandler;
pub use ui::JsonHandler;
pub use ui::MinimalOutputHandler;
pub use event::{
Event, Marker, Message, Scope, ScopeGenerator, SubcommandResult, SupplyScopeGenerator,
TerminateWithFailure,
};
pub(crate) mod event;
pub(crate) mod ui;
mod formatting;
#[cfg(test)]
mod testing;
#[cfg(test)]
pub use testing::{FakeTestReporter, TestReporterWrapper};
pub trait Reporter:
storyteller::EventReporter<Event = Event, Err = storyteller::EventReporterError<Event>>
+ SupplyScopeGenerator
{
fn run_scoped_event<T>(
&self,
event: impl Into<Event>,
action: impl Fn() -> TResult<T>,
) -> TResult<T> {
let event = event.into();
let (start_event, end_event) = event.into_scoped(self.scope_generator());
self.report_event(start_event)?;
let result = action();
self.report_event(end_event)?;
result
}
}
impl<R> Reporter for R where
R: storyteller::EventReporter<Event = Event, Err = storyteller::EventReporterError<Event>>
+ SupplyScopeGenerator
{
}
#[derive(Default)]
pub struct ReporterSetup;
impl ReporterSetup {
pub fn create(self) -> (impl Reporter, impl EventListener<Event = Event>) {
let (sender, receiver) = event_channel::<Event>();
let reporter = MainReporter::new(ChannelReporter::new(sender));
let listener = ChannelEventListener::new(receiver);
(reporter, listener)
}
}
struct MainReporter {
inner: ChannelReporter<Event>,
scope_generator: ScopeCounter,
}
impl MainReporter {
pub fn new(reporter: ChannelReporter<Event>) -> Self {
Self {
inner: reporter,
scope_generator: ScopeCounter::new(),
}
}
}
impl storyteller::EventReporter for MainReporter {
type Event = Event;
type Err = storyteller::EventReporterError<Event>;
fn report_event(&self, event: impl Into<Self::Event>) -> Result<(), Self::Err> {
self.inner.report_event(event)
}
fn disconnect(self) -> Result<(), Self::Err> {
self.inner.disconnect()
}
}
impl SupplyScopeGenerator for MainReporter {
type ScopeGen = ScopeCounter;
fn scope_generator(&self) -> &Self::ScopeGen {
&self.scope_generator
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reporter::event::{Marker, Message, Meta, Scope};
use crate::reporter::TestReporterWrapper;
use crate::{CargoMSRVError, Reporter, SubcommandInit};
use std::collections::BTreeSet;
use storyteller::EventReporter;
#[test]
fn report_successful_scoped_event() {
let reporter = TestReporterWrapper::default();
let content = SubcommandInit::new("find");
let out = reporter
.get()
.run_scoped_event(content.clone(), || TResult::<bool>::Ok(true))
.unwrap();
let events = reporter.wait_for_events();
let start = Event::scoped(
Message::SubcommandInit(content.clone()),
Scope::new(0, Marker::Start),
);
let end = Event::scoped(Message::SubcommandInit(content), Scope::new(0, Marker::End));
assert_eq!(&events, &[start, end]);
assert!(out);
}
#[test]
fn report_failed_scoped_event() {
let reporter = TestReporterWrapper::default();
let content = SubcommandInit::new("find");
let out = reporter
.get()
.run_scoped_event(content.clone(), || {
TResult::<bool>::Err(CargoMSRVError::Storyteller)
})
.unwrap_err();
let events = reporter.wait_for_events();
let start = Event::scoped(
Message::SubcommandInit(content.clone()),
Scope::new(0, Marker::Start),
);
let end = Event::scoped(Message::SubcommandInit(content), Scope::new(0, Marker::End));
assert_eq!(&events, &[start, end]);
assert!(matches!(out, CargoMSRVError::Storyteller));
}
#[test]
fn report_event() {
let setup = ReporterSetup;
let (reporter, _listener) = setup.create();
let result = reporter.report_event(Meta::default());
assert!(result.is_ok());
let disconnect = reporter.disconnect();
assert!(disconnect.is_ok());
}
#[test]
fn scopes() {
use std::iter;
let setup = ReporterSetup;
let (reporter, _listener) = setup.create();
let gen = reporter.scope_generator();
let set = iter::repeat_with(|| gen.generate())
.take(1000)
.map(|(start, end)| {
assert!(start.is_start());
assert!(!end.is_start());
assert_eq!(start.id, end.id);
start.id
})
.collect::<BTreeSet<_>>();
assert_eq!(set.len(), 1000);
}
}