use crate::cli::CliOptions;
use crate::evloop::EventLoop;
use crate::prelude::*;
use crate::state::ops::Operation;
use crate::tests::constant::TempPathCfg;
use crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers};
use jiff::Zoned;
use parking_lot::Mutex;
use std::io::Write;
use std::sync::Arc;
use std::sync::mpsc::{Receiver, channel};
use std::task::{Poll, Waker};
use std::time::Duration;
pub fn make_configs(tp: &TempPathCfg, src: &str) {
std::fs::create_dir_all(tp.xdg_config_home.join("rsvim")).unwrap();
let mut config_entry =
std::fs::File::create(tp.xdg_config_home.join("rsvim").join("rsvim.js"))
.unwrap();
config_entry.write_all(src.as_bytes()).unwrap();
config_entry.flush().unwrap();
}
pub fn make_event_loop(
terminal_cols: u16,
terminal_rows: u16,
cli_opts: CliOptions,
) -> EventLoop {
EventLoop::mock_new(terminal_cols, terminal_rows, cli_opts).unwrap()
}
const INTERVAL_MILLIS: Duration = Duration::from_millis(2);
#[derive(Debug)]
struct SharedWaker {
pub waker: Option<Waker>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MockEvent {
Event(Event),
SleepFor(Duration),
SleepUntil(Zoned),
}
const CTRL_D: Event = Event::Key(KeyEvent::new_with_kind(
KeyCode::Char('d'),
KeyModifiers::CONTROL,
KeyEventKind::Press,
));
#[derive(Debug)]
pub struct MockEventReader {
rx: Receiver<IoResult<Event>>,
shared_waker: Arc<Mutex<SharedWaker>>,
}
impl MockEventReader {
pub fn new(events: Vec<MockEvent>) -> Self {
let (tx, rx) = channel();
let shared_waker = Arc::new(Mutex::new(SharedWaker { waker: None }));
let cloned_shared_waker = shared_waker.clone();
std::thread::spawn(move || {
for (i, event) in events.iter().enumerate() {
trace!("Send mock event[{i}]: {event:?}");
match event {
MockEvent::Event(e) => {
std::thread::sleep(INTERVAL_MILLIS);
tx.send(Ok(e.clone())).unwrap();
}
MockEvent::SleepFor(d) => {
std::thread::sleep(*d);
}
MockEvent::SleepUntil(ts) => {
let now = Zoned::now();
let d = ts.duration_since(&now);
let d = d.as_millis();
if d > 0 {
let d = Duration::from_millis(d as u64);
std::thread::sleep(d);
}
}
}
let mut thread_shared_waker = cloned_shared_waker.lock();
if let Some(waker) = thread_shared_waker.waker.take() {
waker.wake();
}
}
trace!("Send final mock event[{}]: CTRL+D {CTRL_D:?}", events.len());
std::thread::sleep(INTERVAL_MILLIS);
tx.send(Ok(CTRL_D.clone())).unwrap();
let mut thread_shared_waker = cloned_shared_waker.lock();
if let Some(waker) = thread_shared_waker.waker.take() {
waker.wake();
}
});
Self { rx, shared_waker }
}
}
impl futures::Stream for MockEventReader {
type Item = IoResult<Event>;
fn poll_next(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
{
let mut shared_waker = self.shared_waker.lock();
shared_waker.waker = Some(cx.waker().clone());
}
match self.rx.try_recv() {
Ok(event) => Poll::Ready(Some(event)),
_ => Poll::Pending,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MockOperation {
Operation(Operation),
SleepFor(Duration),
SleepUntil(Zoned),
Exit,
}
const EXIT: MockOperation = MockOperation::Exit;
#[derive(Debug)]
pub struct MockOperationReader {
rx: Receiver<IoResult<MockOperation>>,
shared_waker: Arc<Mutex<SharedWaker>>,
}
impl MockOperationReader {
pub fn new(operations: Vec<MockOperation>) -> Self {
let (tx, rx) = channel();
let shared_waker = Arc::new(Mutex::new(SharedWaker { waker: None }));
let cloned_shared_waker = shared_waker.clone();
std::thread::spawn(move || {
for (i, op) in operations.iter().enumerate() {
trace!("Send mock operation[{i}]: {op:?}");
match op {
MockOperation::Operation(op) => {
std::thread::sleep(INTERVAL_MILLIS);
tx.send(Ok(MockOperation::Operation(op.clone()))).unwrap();
}
MockOperation::SleepFor(d) => {
std::thread::sleep(*d);
}
MockOperation::SleepUntil(ts) => {
let now = Zoned::now();
let d = ts.duration_since(&now);
let d = d.as_millis();
if d > 0 {
let d = Duration::from_millis(d as u64);
std::thread::sleep(d);
}
}
MockOperation::Exit => {
std::thread::sleep(INTERVAL_MILLIS);
tx.send(Ok(MockOperation::Exit)).unwrap();
}
}
let mut thread_shared_waker = cloned_shared_waker.lock();
if let Some(waker) = thread_shared_waker.waker.take() {
waker.wake();
}
}
trace!(
"Send final mock operation[{}]: Exit {:?}",
operations.len(),
EXIT
);
std::thread::sleep(INTERVAL_MILLIS);
tx.send(Ok(EXIT.clone())).unwrap();
let mut thread_shared_waker = cloned_shared_waker.lock();
if let Some(waker) = thread_shared_waker.waker.take() {
waker.wake();
}
});
Self { rx, shared_waker }
}
}
impl futures::Stream for MockOperationReader {
type Item = IoResult<MockOperation>;
fn poll_next(
self: std::pin::Pin<&mut Self>,
cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>> {
{
let mut shared_waker = self.shared_waker.lock();
shared_waker.waker = Some(cx.waker().clone());
}
match self.rx.try_recv() {
Ok(op) => Poll::Ready(Some(op)),
_ => Poll::Pending,
}
}
}