use crate::{Error, Result};
use clap::Args;
#[derive(Args)]
#[command(group = clap::ArgGroup::new("question-id").args(&["id", "daily"]).required(true))]
pub struct TestArgs {
#[arg(value_parser = clap::value_parser!(i32))]
pub id: Option<i32>,
pub testcase: Option<String>,
#[arg(short = 'd', long)]
pub daily: bool,
#[arg(short, long)]
pub watch: bool,
}
impl TestArgs {
pub async fn run(&self) -> Result<()> {
use crate::cache::{Cache, Run};
use crate::helper::code_path;
use notify::{Config as NotifyConfig, RecommendedWatcher, RecursiveMode, Watcher};
use std::path::Path;
use std::sync::mpsc::channel;
use std::time::{Duration, Instant};
let cache = Cache::new()?;
let daily_id = if self.daily {
Some(cache.get_daily_problem_id().await?)
} else {
None
};
let id = self.id.or(daily_id).ok_or(Error::NoneError)?;
let case_str: Option<String> = self.testcase.as_ref().map(|case| case.replace("\\n", "\n"));
if self.watch {
let problem = cache.get_problem(id)?;
let path_str = code_path(&problem, None)?;
let path = Path::new(&path_str);
let parent = path
.parent()
.ok_or_else(|| anyhow::anyhow!("Could not get parent directory"))?;
let (tx, rx) = channel();
let mut watcher = RecommendedWatcher::new(tx, NotifyConfig::default())
.map_err(|e| anyhow::anyhow!("Failed to create watcher: {}", e))?;
watcher
.watch(parent, RecursiveMode::NonRecursive)
.map_err(|e| anyhow::anyhow!("Failed to watch parent directory: {}", e))?;
if path.exists() {
println!("Watching for changes in {}...", path_str);
let res = cache.exec_problem(id, Run::Test, case_str.clone()).await?;
println!("{}", res);
} else {
println!("File {} does not exist. Waiting for creation...", path_str);
}
let mut last_run = Instant::now() - Duration::from_secs(1);
while let Ok(res_event) = rx.recv() {
match res_event {
Ok(event) if event.paths.iter().any(|p| p == path) => {
if event.kind.is_create() {
println!("File created, watching for changes...");
} else if event.kind.is_modify() {
if last_run.elapsed() < Duration::from_millis(500) {
continue;
}
std::thread::sleep(Duration::from_millis(200));
while rx.try_recv().is_ok() {}
if !path.exists() {
continue;
}
println!("File changed, testing again...");
match cache.exec_problem(id, Run::Test, case_str.clone()).await {
Ok(res) => println!("{}", res),
Err(e) => println!("Error: {}", e),
}
last_run = Instant::now();
}
}
Err(e) => println!("watch error: {:?}", e),
_ => {}
}
}
} else {
let res = cache.exec_problem(id, Run::Test, case_str).await?;
println!("{}", res);
}
Ok(())
}
}