1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
// 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::{
ffi::OsString,
path::{Path, PathBuf},
};
use bstr::{io::BufReadExt, ByteSlice};
/// Return a sequence of arguments derived from ripgrep rc configuration files.
pub fn args() -> Vec<OsString> {
let config_path = match std::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) => {
message!(
"failed to read the file specified in RIPGREP_CONFIG_PATH: {}",
err
);
return vec![];
}
};
if !errs.is_empty() {
for err in errs {
message!("{}:{}", config_path.display(), err);
}
}
log::debug!(
"{}: arguments loaded from config file: {:?}",
config_path.display(),
args
);
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,
) -> crate::Result<(Vec<OsString>, Vec<Box<dyn std::error::Error>>)> {
let path = path.as_ref();
match std::fs::File::open(&path) {
Ok(file) => parse_reader(file),
Err(err) => Err(From::from(format!("{}: {}", path.display(), err))),
}
}
/// 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: std::io::Read>(
rdr: R,
) -> crate::Result<(Vec<OsString>, Vec<Box<dyn std::error::Error>>)> {
let mut bufrdr = std::io::BufReader::new(rdr);
let (mut args, mut errs) = (vec![], vec![]);
let mut line_number = 0;
bufrdr.for_byte_line_with_terminator(|line| {
line_number += 1;
let line = line.trim();
if line.is_empty() || line[0] == b'#' {
return Ok(true);
}
match line.to_os_str() {
Ok(osstr) => {
args.push(osstr.to_os_string());
}
Err(err) => {
errs.push(format!("{}: {}", line_number, err).into());
}
}
Ok(true)
})?;
Ok((args, errs))
}
#[cfg(test)]
mod tests {
use super::parse_reader;
use std::ffi::OsString;
#[test]
fn basic() {
let (args, errs) = parse_reader(
&b"\
# Test
--context=0
--smart-case
-u
# --bar
--foo
"[..],
)
.unwrap();
assert!(errs.is_empty());
let args: Vec<String> =
args.into_iter().map(|s| s.into_string().unwrap()).collect();
assert_eq!(args, vec!["--context=0", "--smart-case", "-u", "--foo",]);
}
// We test that we can handle invalid UTF-8 on Unix-like systems.
#[test]
#[cfg(unix)]
fn error() {
use std::os::unix::ffi::OsStringExt;
let (args, errs) = parse_reader(
&b"\
quux
foo\xFFbar
baz
"[..],
)
.unwrap();
assert!(errs.is_empty());
assert_eq!(
args,
vec![
OsString::from("quux"),
OsString::from_vec(b"foo\xFFbar".to_vec()),
OsString::from("baz"),
]
);
}
// ... but test that invalid UTF-8 fails on Windows.
#[test]
#[cfg(not(unix))]
fn error() {
let (args, errs) = parse_reader(
&b"\
quux
foo\xFFbar
baz
"[..],
)
.unwrap();
assert_eq!(errs.len(), 1);
assert_eq!(args, vec![OsString::from("quux"), OsString::from("baz"),]);
}
}