PageRenderTime 55ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/crates/core/config.rs

https://github.com/BurntSushi/ripgrep
Rust | 169 lines | 132 code | 12 blank | 25 comment | 6 complexity | dac3a57004f7d6296609dfd96380dbd0 MD5 | raw file
Possible License(s): MIT, Unlicense
  1. // This module provides routines for reading ripgrep config "rc" files. The
  2. // primary output of these routines is a sequence of arguments, where each
  3. // argument corresponds precisely to one shell argument.
  4. use std::env;
  5. use std::error::Error;
  6. use std::ffi::OsString;
  7. use std::fs::File;
  8. use std::io;
  9. use std::path::{Path, PathBuf};
  10. use bstr::{io::BufReadExt, ByteSlice};
  11. use log;
  12. use crate::Result;
  13. /// Return a sequence of arguments derived from ripgrep rc configuration files.
  14. pub fn args() -> Vec<OsString> {
  15. let config_path = match env::var_os("RIPGREP_CONFIG_PATH") {
  16. None => return vec![],
  17. Some(config_path) => {
  18. if config_path.is_empty() {
  19. return vec![];
  20. }
  21. PathBuf::from(config_path)
  22. }
  23. };
  24. let (args, errs) = match parse(&config_path) {
  25. Ok((args, errs)) => (args, errs),
  26. Err(err) => {
  27. message!("{}", err);
  28. return vec![];
  29. }
  30. };
  31. if !errs.is_empty() {
  32. for err in errs {
  33. message!("{}:{}", config_path.display(), err);
  34. }
  35. }
  36. log::debug!(
  37. "{}: arguments loaded from config file: {:?}",
  38. config_path.display(),
  39. args
  40. );
  41. args
  42. }
  43. /// Parse a single ripgrep rc file from the given path.
  44. ///
  45. /// On success, this returns a set of shell arguments, in order, that should
  46. /// be pre-pended to the arguments given to ripgrep at the command line.
  47. ///
  48. /// If the file could not be read, then an error is returned. If there was
  49. /// a problem parsing one or more lines in the file, then errors are returned
  50. /// for each line in addition to successfully parsed arguments.
  51. fn parse<P: AsRef<Path>>(
  52. path: P,
  53. ) -> Result<(Vec<OsString>, Vec<Box<dyn Error>>)> {
  54. let path = path.as_ref();
  55. match File::open(&path) {
  56. Ok(file) => parse_reader(file),
  57. Err(err) => Err(From::from(format!("{}: {}", path.display(), err))),
  58. }
  59. }
  60. /// Parse a single ripgrep rc file from the given reader.
  61. ///
  62. /// Callers should not provided a buffered reader, as this routine will use its
  63. /// own buffer internally.
  64. ///
  65. /// On success, this returns a set of shell arguments, in order, that should
  66. /// be pre-pended to the arguments given to ripgrep at the command line.
  67. ///
  68. /// If the reader could not be read, then an error is returned. If there was a
  69. /// problem parsing one or more lines, then errors are returned for each line
  70. /// in addition to successfully parsed arguments.
  71. fn parse_reader<R: io::Read>(
  72. rdr: R,
  73. ) -> Result<(Vec<OsString>, Vec<Box<dyn Error>>)> {
  74. let bufrdr = io::BufReader::new(rdr);
  75. let (mut args, mut errs) = (vec![], vec![]);
  76. let mut line_number = 0;
  77. bufrdr.for_byte_line_with_terminator(|line| {
  78. line_number += 1;
  79. let line = line.trim();
  80. if line.is_empty() || line[0] == b'#' {
  81. return Ok(true);
  82. }
  83. match line.to_os_str() {
  84. Ok(osstr) => {
  85. args.push(osstr.to_os_string());
  86. }
  87. Err(err) => {
  88. errs.push(format!("{}: {}", line_number, err).into());
  89. }
  90. }
  91. Ok(true)
  92. })?;
  93. Ok((args, errs))
  94. }
  95. #[cfg(test)]
  96. mod tests {
  97. use super::parse_reader;
  98. use std::ffi::OsString;
  99. #[test]
  100. fn basic() {
  101. let (args, errs) = parse_reader(
  102. &b"\
  103. # Test
  104. --context=0
  105. --smart-case
  106. -u
  107. # --bar
  108. --foo
  109. "[..],
  110. )
  111. .unwrap();
  112. assert!(errs.is_empty());
  113. let args: Vec<String> =
  114. args.into_iter().map(|s| s.into_string().unwrap()).collect();
  115. assert_eq!(args, vec!["--context=0", "--smart-case", "-u", "--foo",]);
  116. }
  117. // We test that we can handle invalid UTF-8 on Unix-like systems.
  118. #[test]
  119. #[cfg(unix)]
  120. fn error() {
  121. use std::os::unix::ffi::OsStringExt;
  122. let (args, errs) = parse_reader(
  123. &b"\
  124. quux
  125. foo\xFFbar
  126. baz
  127. "[..],
  128. )
  129. .unwrap();
  130. assert!(errs.is_empty());
  131. assert_eq!(
  132. args,
  133. vec![
  134. OsString::from("quux"),
  135. OsString::from_vec(b"foo\xFFbar".to_vec()),
  136. OsString::from("baz"),
  137. ]
  138. );
  139. }
  140. // ... but test that invalid UTF-8 fails on Windows.
  141. #[test]
  142. #[cfg(not(unix))]
  143. fn error() {
  144. let (args, errs) = parse_reader(
  145. &b"\
  146. quux
  147. foo\xFFbar
  148. baz
  149. "[..],
  150. )
  151. .unwrap();
  152. assert_eq!(errs.len(), 1);
  153. assert_eq!(args, vec![OsString::from("quux"), OsString::from("baz"),]);
  154. }
  155. }