use hirun::runtime::{self, block_on, Builder}; use std::fmt::Debug; use std::fs; use std::path::Path; use std::time::Instant; fn main() { Builder::new().build().unwrap(); let now = Instant::now(); if let Ok(Some(lines)) = block_on(count_entry()) { println!("lines = {lines} cpu = {}", hirun::thread::get_cpu_count()); println!("time: {:?}", now.elapsed()); } } fn help() { println!("{} options [dir] ...", hictor::program_invocation_name()); println!("options:"); println!("--help | -h: print help message"); } async fn count_entry() -> Option { let mut set = runtime::task::JoinSet::new(); let opts = hiopt::options!["help", "h"]; let args = unsafe { hiopt::raw_args_from_i8(hictor::args()) }; for opt in opts.opt_iter(&args[1..]) { let (index, _) = opt.unwrap(); match index { 0..=1 => { help(); return None; } _ => unreachable!(), } } for dir in opts.noopt_iter(&args[1..]) { let _ = set.spawn(visit_dir(dir)).await; } if set.is_empty() { let _ = set.spawn(visit_dir(".")).await; } let mut lines = 0; for (_, ret) in set.wait_all().await { lines += ret.unwrap(); } Some(lines) } #[async_recursion::async_recursion] async fn visit_dir + Send + Debug>(dir: P) -> usize { let mut lines: usize = 0; let mut set = runtime::task::JoinSet::new(); let Ok(dirs) = fs::read_dir(dir) else { return 0; }; for entry in dirs { let path; match entry { Err(_) => continue, Ok(dir) => path = dir.path(), } if path.is_symlink() { continue; } if path.is_dir() { let _ = set.spawn(visit_dir(path)).await; } else if path.is_file() { let _ = set.spawn(count_lines(path)).await; } } for (_, ret) in set.wait_all().await { lines += ret.unwrap(); } lines } async fn count_lines + Send + Debug>(path: P) -> usize { let Ok(data) = fs::read(path) else { return 0; }; let mut lines = if data.is_empty() { 0 } else { 1 }; for b in data { if b == b'\n' { lines += 1; } } lines }