#![cfg_attr(
feature = "tokio",
doc = "# #[tokio::main] async fn main() {")
]
#![cfg_attr(
feature = "tokio",
doc = "sp.finish().await; // without the tokio feature, this is not async",
)]
#![cfg_attr(
not(feature = "tokio"),
doc = "sp.finish(); // with the tokio feature, finish is async"
)]
#![cfg_attr(feature = "tokio", doc = "# }")]
mod animation;
#[cfg(feature = "tokio")]
mod task;
#[cfg(any(test, not(feature = "tokio")))]
mod thread;
pub use animation::{
CHASE,
DOT,
LOOP,
LOOP_WIDE,
SQUARE,
TRIANGLE,
Animate,
Animation
};
mod line;
pub use line::{
Line,
Message,
NoNewLine
};
use crate::{
Buffer,
Claw,
Conciliator,
GetLine,
Input
};
use crate::core::InitialContent;
pub struct Spinner<'c> {
claw: &'c mut Claw,
handle: Handle
}
enum Handle {
Dummy(Claw),
#[cfg(any(test, not(feature = "tokio")))]
Thread(thread::Handle),
#[cfg(feature = "tokio")]
Task(task::Handle)
}
impl<'c> Spinner<'c> {
pub fn new<A, I>(claw: &'c mut Claw, animate: A, message: I) -> Self
where A: Animate + Send + 'static,
I: InitialContent
{
let mut msg = claw.err.buffer();
message.init_buffer(&mut msg);
let handle = Handle::spawn(claw.clone(), animate, msg);
Self { claw, handle }
}
pub(crate) fn print_buffer(&self, buffer: Buffer) {
self.handle.send_line(buffer);
}
pub(crate) fn set_message(&self, buffer: Buffer) {
self.handle.send_message(buffer);
}
pub fn message<I: InitialContent>(&self, init: I) -> Message {
let mut msg = Message::new(self);
init.init_buffer(&mut msg);
msg
}
#[cfg_attr(
feature = "tokio",
doc = "# #[tokio::main] async fn main() {")
]
#[cfg_attr(feature = "tokio", doc = "# }")]
#[cfg(not(feature = "tokio"))]
pub fn input<I: Input>(&self, mut request: I) -> I::T {
request.print(self);
loop {
request.prompt(&mut self.message(..));
let input_string = self.handle.request_input();
if let Some(thing) = request.validate(&input_string) {
return thing
}
}
}
#[cfg_attr(
feature = "tokio",
doc = "# #[tokio::main] async fn main() {")
]
#[cfg_attr(feature = "tokio", doc = "# }")]
#[cfg(feature = "tokio")]
pub async fn input<I: Input>(&self, mut request: I) -> I::T {
request.print(self);
loop {
request.prompt(&mut self.message(..));
let input_string = self.handle.request_input().await;
if let Some(thing) = request.validate(&input_string) {
return thing
}
}
}
#[cfg(not(feature = "tokio"))]
pub fn finish(self) {
self.handle.finish();
}
#[cfg(feature = "tokio")]
pub async fn finish(self) {
self.handle.finish().await;
}
#[cfg(not(feature = "tokio"))]
pub fn clear(self) {
self.handle.clear();
}
#[cfg(feature = "tokio")]
pub async fn clear(self) {
self.handle.clear().await;
}
#[cfg(test)]
fn spawn_dummy(claw: &'c mut Claw) -> Self {
let handle = Handle::Dummy(claw.clone());
Self { claw, handle }
}
#[cfg(not(feature = "tokio"))]
#[cfg(test)]
fn spawn_thread<A>(claw: &'c mut Claw, anim: A) -> Self
where A: Animate + Send + 'static
{
let msg = claw.err.buffer();
let handle = Handle::Thread(
thread::Handle::spawn(claw.clone(), anim, msg)
);
Self { claw, handle }
}
#[cfg(all(test, feature = "tokio"))]
fn spawn_task<A>(claw: &'c mut Claw, anim: A) -> Self
where A: Animate + Send + 'static
{
let msg = claw.err.buffer();
let handle = Handle::Task(
task::Handle::spawn(claw.clone(), anim, msg)
);
Self { claw, handle }
}
}
impl<'l, 'c> GetLine<'l> for Spinner<'c> {
type Line = Line<'l>;
fn get_line(&'l self) -> Self::Line {Line::new(self)}
}
impl<'c> Conciliator for Spinner<'c> {}
impl Handle {
pub(crate) fn spawn<A>(claw: Claw, anim: A, msg: Buffer) -> Self
where A: Animate + Send + 'static
{
if !claw.err.colors_enabled() {
return Self::Dummy(claw);
}
#[cfg(feature = "tokio")] {
#[cfg(test)]
match tokio::runtime::Handle::try_current().is_ok() {
true => Self::Task(task::Handle::spawn(claw, anim, msg)),
false => Self::Thread(thread::Handle::spawn(claw, anim, msg))
}
#[cfg(not(test))]
Self::Task(task::Handle::spawn(claw, anim, msg))
}
#[cfg(not(feature = "tokio"))] {
Self::Thread(thread::Handle::spawn(claw, anim, msg))
}
}
pub(crate) fn send_line(&self, buffer: Buffer) {
match self {
Self::Dummy(claw) => claw.out.print_buffer(&buffer).unwrap(),
#[cfg(any(test, not(feature = "tokio")))]
Self::Thread(handle) => handle.send_line(buffer),
#[cfg(feature = "tokio")]
Self::Task(handle) => handle.send_line(buffer)
}
}
#[cfg(feature = "tokio")]
pub(crate) async fn request_input(&self) -> String {
match self {
Self::Dummy(_claw) => {
let mut input_buf = String::new();
std::io::stdin().read_line(&mut input_buf).unwrap();
input_buf
},
#[cfg(test)]
Self::Thread(handle) => handle.request_input(),
Self::Task(handle) => handle.request_input().await
}
}
#[cfg(not(feature = "tokio"))]
pub(crate) fn request_input(&self) -> String {
match self {
Self::Dummy(_claw) => {
let mut input_buf = String::new();
std::io::stdin().read_line(&mut input_buf).unwrap();
input_buf
},
Self::Thread(handle) => handle.request_input(),
}
}
pub(crate) fn send_message(&self, buffer: Buffer) {
match self {
Self::Dummy(_claw) => {},
#[cfg(any(test, not(feature = "tokio")))]
Self::Thread(handle) => handle.send_message(buffer),
#[cfg(feature = "tokio")]
Self::Task(handle) => handle.send_message(buffer)
}
}
#[cfg(feature = "tokio")]
pub(crate) async fn finish(self) {
match self {
Self::Dummy(_claw) => {},
#[cfg(test)]
Self::Thread(handle) => handle.finish(),
Self::Task(handle) => handle.finish().await
}
}
#[cfg(feature = "tokio")]
pub(crate) async fn clear(self) {
match self {
Self::Dummy(_claw) => {},
#[cfg(test)]
Self::Thread(handle) => handle.clear(),
Self::Task(handle) => handle.clear().await
}
}
#[cfg(not(feature = "tokio"))]
pub(crate) fn finish(self) {
match self {
Self::Dummy(_claw) => {},
Self::Thread(handle) => handle.finish(),
}
}
#[cfg(not(feature = "tokio"))]
pub(crate) fn clear(self) {
match self {
Self::Dummy(_claw) => {},
Self::Thread(handle) => handle.clear(),
}
}
}
#[test]
fn dummy_spin() {
let mut con = crate::init();
let sp = Spinner::spawn_dummy(&mut con);
drop(sp);
}
#[cfg(not(feature = "tokio"))]
#[test]
fn thread_spin() {
let mut con = crate::init();
let sp = Spinner::spawn_thread(&mut con, LOOP);
drop(sp);
let sp = Spinner::spawn_thread(&mut con, LOOP);
sp.clear();
let sp = Spinner::spawn_thread(&mut con, LOOP);
sp.finish();
}
#[cfg(feature = "tokio")]
#[tokio::test]
async fn task_spin() {
let mut con = crate::init();
let sp = Spinner::spawn_task(&mut con, SQUARE);
sp.clear().await;
let sp = Spinner::spawn_task(&mut con, SQUARE);
sp.finish().await;
let sp = Spinner::spawn_task(&mut con, SQUARE);
drop(sp);
}