2018-02-04 03:33:52 +02:00
|
|
|
// This module provides routines for reading ripgrep config "rc" files. The
|
|
|
|
// primary output of these routines is a sequence of arguments, where each
|
|
|
|
// argument corresponds precisely to one shell argument.
|
|
|
|
|
|
|
|
use std::env;
|
|
|
|
use std::error::Error;
|
2020-02-18 01:08:47 +02:00
|
|
|
use std::ffi::OsString;
|
2018-02-04 03:33:52 +02:00
|
|
|
use std::fs::File;
|
2019-04-04 21:14:29 +02:00
|
|
|
use std::io;
|
2018-02-04 03:33:52 +02:00
|
|
|
use std::path::{Path, PathBuf};
|
|
|
|
|
2019-06-26 22:47:33 +02:00
|
|
|
use bstr::{io::BufReadExt, ByteSlice};
|
2019-01-19 17:15:56 +02:00
|
|
|
use log;
|
|
|
|
|
|
|
|
use crate::Result;
|
2018-02-04 03:33:52 +02:00
|
|
|
|
|
|
|
/// Return a sequence of arguments derived from ripgrep rc configuration files.
|
2018-08-03 23:26:22 +02:00
|
|
|
pub fn args() -> Vec<OsString> {
|
2018-02-04 03:33:52 +02:00
|
|
|
let config_path = match env::var_os("RIPGREP_CONFIG_PATH") {
|
|
|
|
None => return vec![],
|
|
|
|
Some(config_path) => {
|
|
|
|
if config_path.is_empty() {
|
|
|
|
return vec![];
|
|
|
|
}
|
|
|
|
PathBuf::from(config_path)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let (args, errs) = match parse(&config_path) {
|
|
|
|
Ok((args, errs)) => (args, errs),
|
|
|
|
Err(err) => {
|
2021-11-15 17:29:34 +02:00
|
|
|
message!(
|
|
|
|
"failed to read the file specified in RIPGREP_CONFIG_PATH: {}",
|
|
|
|
err
|
|
|
|
);
|
2018-02-04 03:33:52 +02:00
|
|
|
return vec![];
|
|
|
|
}
|
|
|
|
};
|
2018-08-03 23:26:22 +02:00
|
|
|
if !errs.is_empty() {
|
2018-02-04 03:33:52 +02:00
|
|
|
for err in errs {
|
2018-08-03 23:26:22 +02:00
|
|
|
message!("{}:{}", config_path.display(), err);
|
2018-02-04 03:33:52 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-19 17:15:56 +02:00
|
|
|
log::debug!(
|
2018-02-04 03:33:52 +02:00
|
|
|
"{}: arguments loaded from config file: {:?}",
|
2018-08-03 23:26:22 +02:00
|
|
|
config_path.display(),
|
|
|
|
args
|
|
|
|
);
|
2018-02-04 03:33:52 +02:00
|
|
|
args
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a single ripgrep rc file from the given path.
|
|
|
|
///
|
|
|
|
/// On success, this returns a set of shell arguments, in order, that should
|
|
|
|
/// be pre-pended to the arguments given to ripgrep at the command line.
|
|
|
|
///
|
|
|
|
/// If the file could not be read, then an error is returned. If there was
|
|
|
|
/// a problem parsing one or more lines in the file, then errors are returned
|
|
|
|
/// for each line in addition to successfully parsed arguments.
|
|
|
|
fn parse<P: AsRef<Path>>(
|
|
|
|
path: P,
|
2019-06-17 00:37:51 +02:00
|
|
|
) -> Result<(Vec<OsString>, Vec<Box<dyn Error>>)> {
|
2018-02-04 03:33:52 +02:00
|
|
|
let path = path.as_ref();
|
|
|
|
match File::open(&path) {
|
|
|
|
Ok(file) => parse_reader(file),
|
2018-08-03 23:26:22 +02:00
|
|
|
Err(err) => Err(From::from(format!("{}: {}", path.display(), err))),
|
2018-02-04 03:33:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse a single ripgrep rc file from the given reader.
|
|
|
|
///
|
|
|
|
/// Callers should not provided a buffered reader, as this routine will use its
|
|
|
|
/// own buffer internally.
|
|
|
|
///
|
|
|
|
/// On success, this returns a set of shell arguments, in order, that should
|
|
|
|
/// be pre-pended to the arguments given to ripgrep at the command line.
|
|
|
|
///
|
|
|
|
/// If the reader could not be read, then an error is returned. If there was a
|
|
|
|
/// problem parsing one or more lines, then errors are returned for each line
|
|
|
|
/// in addition to successfully parsed arguments.
|
|
|
|
fn parse_reader<R: io::Read>(
|
|
|
|
rdr: R,
|
2019-06-17 00:37:51 +02:00
|
|
|
) -> Result<(Vec<OsString>, Vec<Box<dyn Error>>)> {
|
2019-04-04 21:14:29 +02:00
|
|
|
let bufrdr = io::BufReader::new(rdr);
|
2018-02-04 03:33:52 +02:00
|
|
|
let (mut args, mut errs) = (vec![], vec![]);
|
|
|
|
let mut line_number = 0;
|
2019-04-04 21:14:29 +02:00
|
|
|
bufrdr.for_byte_line_with_terminator(|line| {
|
2018-02-04 03:33:52 +02:00
|
|
|
line_number += 1;
|
2019-04-04 21:14:29 +02:00
|
|
|
|
|
|
|
let line = line.trim();
|
2018-02-04 03:33:52 +02:00
|
|
|
if line.is_empty() || line[0] == b'#' {
|
2019-04-04 21:14:29 +02:00
|
|
|
return Ok(true);
|
2018-02-04 03:33:52 +02:00
|
|
|
}
|
2019-04-04 21:14:29 +02:00
|
|
|
match line.to_os_str() {
|
2018-02-04 03:33:52 +02:00
|
|
|
Ok(osstr) => {
|
2019-04-04 21:14:29 +02:00
|
|
|
args.push(osstr.to_os_string());
|
2018-02-04 03:33:52 +02:00
|
|
|
}
|
|
|
|
Err(err) => {
|
|
|
|
errs.push(format!("{}: {}", line_number, err).into());
|
|
|
|
}
|
|
|
|
}
|
2019-04-04 21:14:29 +02:00
|
|
|
Ok(true)
|
|
|
|
})?;
|
2018-02-04 03:33:52 +02:00
|
|
|
Ok((args, errs))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::parse_reader;
|
2020-02-18 01:08:47 +02:00
|
|
|
use std::ffi::OsString;
|
2018-02-04 03:33:52 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn basic() {
|
2020-02-18 01:08:47 +02:00
|
|
|
let (args, errs) = parse_reader(
|
|
|
|
&b"\
|
2018-02-04 03:33:52 +02:00
|
|
|
# Test
|
|
|
|
--context=0
|
|
|
|
--smart-case
|
|
|
|
-u
|
|
|
|
|
|
|
|
|
|
|
|
# --bar
|
|
|
|
--foo
|
2020-02-18 01:08:47 +02:00
|
|
|
"[..],
|
|
|
|
)
|
|
|
|
.unwrap();
|
2018-02-04 03:33:52 +02:00
|
|
|
assert!(errs.is_empty());
|
|
|
|
let args: Vec<String> =
|
|
|
|
args.into_iter().map(|s| s.into_string().unwrap()).collect();
|
2020-02-18 01:08:47 +02:00
|
|
|
assert_eq!(args, vec!["--context=0", "--smart-case", "-u", "--foo",]);
|
2018-02-04 03:33:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// We test that we can handle invalid UTF-8 on Unix-like systems.
|
|
|
|
#[test]
|
|
|
|
#[cfg(unix)]
|
|
|
|
fn error() {
|
|
|
|
use std::os::unix::ffi::OsStringExt;
|
|
|
|
|
2020-02-18 01:08:47 +02:00
|
|
|
let (args, errs) = parse_reader(
|
|
|
|
&b"\
|
2018-02-04 03:33:52 +02:00
|
|
|
quux
|
|
|
|
foo\xFFbar
|
|
|
|
baz
|
2020-02-18 01:08:47 +02:00
|
|
|
"[..],
|
|
|
|
)
|
|
|
|
.unwrap();
|
2018-02-04 03:33:52 +02:00
|
|
|
assert!(errs.is_empty());
|
2020-02-18 01:08:47 +02:00
|
|
|
assert_eq!(
|
|
|
|
args,
|
|
|
|
vec![
|
|
|
|
OsString::from("quux"),
|
|
|
|
OsString::from_vec(b"foo\xFFbar".to_vec()),
|
|
|
|
OsString::from("baz"),
|
|
|
|
]
|
|
|
|
);
|
2018-02-04 03:33:52 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// ... but test that invalid UTF-8 fails on Windows.
|
|
|
|
#[test]
|
|
|
|
#[cfg(not(unix))]
|
|
|
|
fn error() {
|
2020-02-18 01:08:47 +02:00
|
|
|
let (args, errs) = parse_reader(
|
|
|
|
&b"\
|
2018-02-04 03:33:52 +02:00
|
|
|
quux
|
|
|
|
foo\xFFbar
|
|
|
|
baz
|
2020-02-18 01:08:47 +02:00
|
|
|
"[..],
|
|
|
|
)
|
|
|
|
.unwrap();
|
2018-02-04 03:33:52 +02:00
|
|
|
assert_eq!(errs.len(), 1);
|
2020-02-18 01:08:47 +02:00
|
|
|
assert_eq!(args, vec![OsString::from("quux"), OsString::from("baz"),]);
|
2018-02-04 03:33:52 +02:00
|
|
|
}
|
|
|
|
}
|