main.rs 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. use walkdir::WalkDir;
  2. #[derive(Debug)]
  3. struct Args {
  4. targets: Vec<String>,
  5. num_threads: u8,
  6. }
  7. impl Args {
  8. fn parse() -> Args {
  9. let matches = clap::App::new("rubygrep")
  10. .version("0.1")
  11. .author("Getty Ritter <rubygrep@infinitenegativeutility.com>")
  12. .about("Search Ruby source trees")
  13. .arg(clap::Arg::with_name("threads")
  14. .short("j")
  15. .value_name("THREADS")
  16. .help("Spawn the specified number of worker threads")
  17. .takes_value(true))
  18. .arg(clap::Arg::with_name("targets")
  19. .help("The Ruby sources and directories to search")
  20. .multiple(true))
  21. .get_matches();
  22. let targets = if let Some(values) = matches.values_of("targets") {
  23. values.map(|x| x.to_owned()).collect::<Vec<String>>()
  24. } else {
  25. vec![".".to_owned()]
  26. };
  27. let num_threads = if let Some(val) = matches.value_of("threads") {
  28. match val.parse() {
  29. Ok(x) => x,
  30. Err(_) => {
  31. panic!("Invalid number of threads: {}", val);
  32. }
  33. }
  34. } else {
  35. 8
  36. };
  37. Args { targets, num_threads }
  38. }
  39. }
  40. fn is_ruby_source(path: &std::path::Path) -> bool {
  41. if let Some(ext) = path.extension() {
  42. return ext == "rb";
  43. }
  44. false
  45. }
  46. fn main() {
  47. let args = Args::parse();
  48. println!("Got args: {:?}", args);
  49. let Args {
  50. targets,
  51. num_threads,
  52. } = args;
  53. let (work_send, work_recv) = crossbeam::channel::unbounded();
  54. // produce a thread which populates the channel with work
  55. let producer = std::thread::spawn(move || {
  56. for target in targets {
  57. for entry in WalkDir::new(target).into_iter().filter_map(|e| e.ok()) {
  58. if is_ruby_source(entry.path()) {
  59. work_send.send(entry).expect("Unable to send work from producer thread");
  60. }
  61. }
  62. }
  63. });
  64. // produce a set of threads which can grab work to be done
  65. let workers: Vec<std::thread::JoinHandle<()>> = (0..num_threads).map(|id| {
  66. let receiver = work_recv.clone();
  67. std::thread::spawn(move || {
  68. while let Ok(msg) = receiver.recv() {
  69. use std::io::Read;
  70. // println!("thread {} processing {:?}", id, msg);
  71. let mut buf = Vec::new();
  72. {
  73. let mut f = std::fs::File::open(msg.path()).expect("Unable to read file");
  74. f.read_to_end(&mut buf).expect("Unable to read file");
  75. }
  76. let parser = lib_ruby_parser::Parser::new(buf, std::default::Default::default());
  77. let lib_ruby_parser::ParserResult { ast, diagnostics, .. } = parser.do_parse();
  78. if let Some(_ast) = ast {
  79. // println!("Got ast: {:?}", ast);
  80. } else {
  81. println!("Unable to parse {:?}", msg.path());
  82. for d in diagnostics {
  83. println!(" - {:?}", d);
  84. }
  85. }
  86. }
  87. println!("thread {} done", id);
  88. })
  89. }).collect();
  90. // join all the threads
  91. producer.join().expect("Producer thread panicked!");
  92. for w in workers {
  93. w.join().expect("Worker thread panicked!");
  94. }
  95. }