A cli program to easily handle .gitignore files
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

84 lines
2.8 KiB

4 years ago
  1. //! This module contains an abstraction for gitignore files
  2. use crate::errors::*;
  3. use crate::helpers::git_dir;
  4. use log::trace;
  5. use std::fs::OpenOptions;
  6. use std::path::PathBuf;
  7. /// An abstraction of the gitignore file
  8. #[derive(Debug, Default)]
  9. pub struct Gitignore {
  10. /// The path to the gitignore
  11. path: PathBuf,
  12. }
  13. impl Gitignore {
  14. /// Create a new Gitignore struct from a git root folder path
  15. ///
  16. /// The given `path` must be a valid path to an existing git root folder
  17. pub fn from_path(path: &PathBuf) -> Self {
  18. trace!("Creating gitignore file object for path {}", path.to_string_lossy());
  19. Gitignore { path: path.clone() }
  20. }
  21. /// Return a new `Gitignore` object from the default location
  22. ///
  23. /// This function gets the default .gitignore in the git root folder for the current working
  24. /// directory
  25. ///
  26. /// # Errors
  27. ///
  28. /// - `ErrorKind::NoGitRootFound` when there was .gitignore file found at the default location
  29. pub fn from_default_path() -> Result<Self> {
  30. let mut path = git_dir()?.ok_or(ErrorKind::NoGitRootFound)?;
  31. path.push(".gitignore");
  32. Ok(Self::from_path(&path))
  33. }
  34. /// Append a line to the file
  35. pub fn add_line(&self, line: &str) -> Result<()> {
  36. use std::io::prelude::*;
  37. let mut file = OpenOptions::new()
  38. .write(true)
  39. .read(true)
  40. .append(true)
  41. .create(true)
  42. .open(&self.path)
  43. .chain_err(|| "Error while opening gitignore file")?;
  44. let file_len = file.stream_len()?;
  45. let newline = if file_len > 0 {
  46. file.seek(std::io::SeekFrom::End(-1))?;
  47. let mut buf: Vec<u8> = vec![0; 1];
  48. file.read_exact(&mut buf)?;
  49. if buf[0] == '\n' as u8 {
  50. // The last line already ends with a newline
  51. ""
  52. } else {
  53. // Add a newline if the last line doesn't end with one
  54. "\n"
  55. }
  56. } else {
  57. // The file is empty, so we don't need a newline
  58. ""
  59. };
  60. writeln!(file, "{}{}", newline, line)
  61. .chain_err(|| "Error while writing line to gitignore")?;
  62. Ok(())
  63. }
  64. /// Reads the contents of the gitignore file and adds them to buf
  65. pub fn contents(&self, buf: &mut String) -> Result<()> {
  66. use std::io::prelude::*;
  67. let mut file = OpenOptions::new()
  68. .read(true)
  69. .open(&self.path)
  70. .chain_err(|| "Error while opening gitignore file")?;
  71. file.read_to_string(buf)?;
  72. Ok(())
  73. }
  74. }
  75. impl std::fmt::Display for Gitignore {
  76. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
  77. write!(f, "gitignore file {}", self.path.to_string_lossy())
  78. }
  79. }