1extern crate shlex;
2extern crate getopts;
3
4use std::env;
5use getopts::Options;
6use std::process::exit;
7use std::process::Command;
8
9fn print_usage(program: &str, opts: Options) {
10 let brief = format!("Usage: {} <from> <to> [target_files]", program);
11 print!("{}", opts.usage(&brief));
12}
13
14fn print_version() {
15 println!("{}", env!("CARGO_PKG_VERSION"));
16}
17
18fn is_gsed_installed() -> bool {
19 Command::new("which")
20 .arg("gsed")
21 .output()
22 .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) })
23 .status
24 .success()
25}
26
27fn quote_args(args: &Vec<String>) -> (String, String, String) {
28 let quoted_args: Vec<String> = args[1..].iter().map(|x| shlex::quote(x).to_string() ).collect();
29 (
30 quoted_args[0].clone(),
31 quoted_args[1].clone(),
32 if quoted_args.len() > 2 { quoted_args[2].clone() } else { ".".to_string() }
33 )
34}
35
36pub fn substitute(args: &Vec<String>) -> () {
37 let (from, to, path) = quote_args(&args);
38
39 let output = Command::new("git")
40 .args(&["grep", "-l"])
41 .arg(&from)
42 .arg(&path)
43 .output()
44 .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
45
46 let stdout = String::from_utf8_lossy(&output.stdout);
47 let target_files: Vec<&str> = stdout.lines_any().collect();
48 if target_files.len() == 0 { exit(0); }
49 let re = format!("s/{}/{}/g", &from, &to);
50
51 if is_gsed_installed() {
52 Command::new("gsed")
53 .arg("-i")
54 .arg(&re)
55 .args(&target_files)
56 .status()
57 .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
58 } else {
59 Command::new("sed")
60 .args(&["-i", "", "-e"])
61 .arg(&re)
62 .args(&target_files)
63 .status()
64 .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) });
65 }
66}
67
68pub fn run(args: env::Args) -> () {
69 let args: Vec<String> = args.collect();
70 let ref program = args[0];
71
72 let mut opts = Options::new();
73 opts.optflag("h", "help", "print this help menu");
74 opts.optflag("v", "version", "print version");
75
76 let matches = match opts.parse(&args[1..]) {
77 Ok(m) => { m }
78 Err(f) => { panic!(f.to_string()) }
79 };
80
81 if matches.opt_present("v") {
82 return print_version();
83 } else if matches.opt_present("h") || args.len() <= 2 {
84 return print_usage(&program, opts);
85 }
86
87 substitute(&args);
88}