use std::{
env::current_dir,
path::{Path, PathBuf},
sync::mpsc::{channel, Receiver, RecvError, Sender},
};
use crossterm::event::{read, Event, KeyCode, KeyEvent, KeyModifiers};
use notify::DebouncedEvent;
use walkdir::WalkDir;
use crate::Message;
pub fn filter_rs(path: impl AsRef<Path>) -> bool {
path.as_ref().extension().map_or(false, |ext| ext == "rs")
}
pub fn filter_plain(path: impl AsRef<Path>) -> bool {
if path.as_ref().ancestors().count() != 3 {
return false;
}
filter_rs(path)
}
pub fn filter_cargo(path: impl AsRef<Path>) -> bool {
Path::new("exercises")
.join(path.as_ref())
.join("Cargo.toml")
.exists()
}
pub fn notify_adapter(rx: &Receiver<DebouncedEvent>, tx: &Sender<Message>) -> anyhow::Result<()> {
loop {
match rx.recv() {
Err(RecvError) => break,
Ok(DebouncedEvent::Write(b)) => {
let b = b
.strip_prefix(current_dir().unwrap().join("exercises"))
.expect("events to be in subfolder")
.to_path_buf();
let msg = Message::Notify(b);
tx.send(msg.clone())
.map_err(|_| anyhow::anyhow!("failed to send {:?}", msg))?;
}
Ok(DebouncedEvent::Error(e, _path)) => return Err(e.into()),
_ => {}
}
}
Ok(())
}
pub fn transmit_input(message_sender: &Sender<Message>) -> anyhow::Result<()> {
let (tx, rx) = channel();
loop {
match read()? {
Event::Key(KeyEvent {
code: KeyCode::Char('c'),
modifiers: KeyModifiers::CONTROL,
}) => {
message_sender
.send(Message::Terminate)
.map_err(|_| anyhow::anyhow!("transmit_input unable to send Terminate"))?;
break;
}
key => {
if message_sender
.send(Message::KeyEvent(key, tx.clone()))
.is_err()
{
break;
}
}
}
if rx.recv()? {
break;
}
}
Ok(())
}
pub fn list_exercises(path: impl AsRef<Path>) -> impl Iterator<Item = PathBuf> {
WalkDir::new(&path)
.min_depth(2)
.sort_by_file_name()
.into_iter()
.filter_map(Result::ok)
.map(move |de| {
de.into_path()
.strip_prefix(&path)
.expect("dir entry to start with `path`")
.to_owned()
})
.filter(|pb| filter_plain(pb) || filter_cargo(pb))
}