use is_terminal::IsTerminal;
use pin_project::pin_project;
use std::pin::Pin;
use std::task::{Context, Poll};
use tokio::io::Stdin;
use tokio::io::{self, AsyncBufReadExt, BufReader};
use tokio_stream::Stream;
use tokio_stream::wrappers::LinesStream;
use tracing::instrument;
#[pin_project(project = UserStreamProj)]
#[derive(Debug, Default)]
pub enum UserStream {
Real(LinesStream<BufReader<Stdin>>),
#[default]
Virtual,
}
impl UserStream {
pub fn new_real() -> Option<UserStream> {
let stdin = io::stdin();
if stdin.is_terminal() {
let bufstdin = BufReader::new(stdin);
let linesstream = LinesStream::new(bufstdin.lines());
Some(UserStream::Real(linesstream))
} else {
None
}
}
pub fn new_virtual() -> UserStream {
UserStream::Virtual
}
}
impl Stream for UserStream {
type Item = String;
#[instrument(level = "debug", ret, skip(cx))]
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.project();
match this {
UserStreamProj::Real(linesstream) => {
let next = Pin::new(linesstream).poll_next(cx);
match next {
Poll::Ready(Some(Ok(s))) => Poll::Ready(Some(s)),
Poll::Ready(None) => Poll::Ready(None),
Poll::Pending => Poll::Pending,
Poll::Ready(_) => Poll::Ready(None),
}
}
UserStreamProj::Virtual {} => Poll::Ready(None),
}
}
}