use std::hash::{DefaultHasher, Hash, Hasher};
use std::io;
use crossterm::event::{Event, EventStream};
use futures::{StreamExt, stream::BoxStream};
use super::{SubscriptionId, SubscriptionSource};
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
pub struct TerminalEvents;
impl TerminalEvents {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl SubscriptionSource for TerminalEvents {
type Output = Result<Event, io::Error>;
fn stream(&self) -> BoxStream<'static, Self::Output> {
let stream = EventStream::new();
futures::stream::unfold(stream, |mut stream| async move {
stream.next().await.map(|result| {
match &result {
Ok(event) => {
tracing::trace!(
target: "tears::subscription::terminal",
event_type = trace_event_type(event),
"terminal event received"
);
}
Err(error) => {
tracing::debug!(
target: "tears::subscription::terminal",
error_kind = ?error.kind(),
"terminal event read failed"
);
}
}
(result, stream)
})
})
.boxed()
}
fn id(&self) -> SubscriptionId {
let mut hasher = DefaultHasher::new();
self.hash(&mut hasher);
SubscriptionId::of::<Self>(hasher.finish())
}
}
const fn trace_event_type(event: &Event) -> &'static str {
match event {
Event::FocusGained => "focus_gained",
Event::FocusLost => "focus_lost",
Event::Key(_) => "key",
Event::Mouse(_) => "mouse",
Event::Paste(_) => "paste",
Event::Resize(_, _) => "resize",
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_terminal_events_new() {
let events = TerminalEvents::new();
assert_eq!(events, TerminalEvents);
}
#[test]
fn test_terminal_events_id_consistency() {
let events1 = TerminalEvents::new();
let events2 = TerminalEvents::new();
assert_eq!(events1.id(), events2.id());
}
}