gnostr_asyncgit/sync/
ignore.rs1use std::{
2 fs::{File, OpenOptions},
3 io::{Read, Seek, SeekFrom, Write},
4 path::Path,
5};
6
7use scopetime::scope_time;
8
9use super::{utils::work_dir, RepoPath};
10use crate::{
11 error::{Error, Result},
12 sync::repository::repo,
13};
14
15static GITIGNORE: &str = ".gitignore";
16
17pub fn add_to_ignore(repo_path: &RepoPath, path_to_ignore: &str) -> Result<()> {
19 scope_time!("add_to_ignore");
20
21 let repo = repo(repo_path)?;
22
23 if Path::new(path_to_ignore).file_name() == Path::new(GITIGNORE).file_name() {
24 return Err(Error::Generic(String::from("cannot ignore gitignore")));
25 }
26
27 let ignore_file = work_dir(&repo)?.join(GITIGNORE);
28
29 let optional_newline = ignore_file.exists() && !file_ends_with_newline(&ignore_file)?;
30
31 let mut file = OpenOptions::new()
32 .append(true)
33 .create(true)
34 .open(ignore_file)?;
35
36 writeln!(
37 file,
38 "{}{}",
39 if optional_newline { "\n" } else { "" },
40 path_to_ignore
41 )?;
42
43 Ok(())
44}
45
46fn file_ends_with_newline(file: &Path) -> Result<bool> {
47 let mut file = File::open(file)?;
48 let size = file.metadata()?.len();
49
50 file.seek(SeekFrom::Start(size.saturating_sub(1)))?;
51 let mut last_char = String::with_capacity(1);
52 file.read_to_string(&mut last_char)?;
53
54 Ok(last_char == "\n")
55}
56
57#[cfg(test)]
58mod tests {
59 use std::{fs::File, io, path::Path};
60
61 use io::BufRead;
62 use pretty_assertions::assert_eq;
63
64 use super::*;
65 use crate::sync::{tests::repo_init, utils::repo_write_file};
66
67 #[test]
68 fn test_empty() -> Result<()> {
69 let ignore_file_path = Path::new(".gitignore");
70 let file_path = Path::new("foo.txt");
71 let (_td, repo) = repo_init()?;
72 let root = repo.path().parent().unwrap();
73 let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into();
74
75 File::create(root.join(file_path))?.write_all(b"test")?;
76
77 assert_eq!(root.join(ignore_file_path).exists(), false);
78 add_to_ignore(repo_path, file_path.to_str().unwrap())?;
79 assert_eq!(root.join(ignore_file_path).exists(), true);
80
81 Ok(())
82 }
83
84 fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>>
85 where
86 P: AsRef<Path>,
87 {
88 let file = File::open(filename)?;
89 Ok(io::BufReader::new(file).lines())
90 }
91
92 #[test]
93 fn test_append() -> Result<()> {
94 let ignore_file_path = Path::new(".gitignore");
95 let file_path = Path::new("foo.txt");
96 let (_td, repo) = repo_init()?;
97 let root = repo.path().parent().unwrap();
98 let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into();
99
100 File::create(root.join(file_path))?.write_all(b"test")?;
101 File::create(root.join(ignore_file_path))?.write_all(b"foo\n")?;
102
103 add_to_ignore(repo_path, file_path.to_str().unwrap())?;
104
105 let mut lines = read_lines(root.join(ignore_file_path)).unwrap();
106 assert_eq!(&lines.nth(1).unwrap().unwrap(), "foo.txt");
107
108 Ok(())
109 }
110
111 #[test]
112 fn test_append_no_newline_at_end() -> Result<()> {
113 let ignore_file_path = Path::new(".gitignore");
114 let file_path = Path::new("foo.txt");
115 let (_td, repo) = repo_init()?;
116 let root = repo.path().parent().unwrap();
117 let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into();
118
119 File::create(root.join(file_path))?.write_all(b"test")?;
120 File::create(root.join(ignore_file_path))?.write_all(b"foo")?;
121
122 add_to_ignore(repo_path, file_path.to_str().unwrap())?;
123
124 let mut lines = read_lines(root.join(ignore_file_path)).unwrap();
125 assert_eq!(&lines.nth(1).unwrap().unwrap(), "foo.txt");
126
127 Ok(())
128 }
129
130 #[test]
131 fn test_ignore_ignore() {
132 let ignore_file_path = Path::new(".gitignore");
133 let (_td, repo) = repo_init().unwrap();
134 let root = repo.path().parent().unwrap();
135 let repo_path: &RepoPath = &root.as_os_str().to_str().unwrap().into();
136
137 repo_write_file(&repo, ".gitignore", "#foo").unwrap();
138
139 let res = add_to_ignore(repo_path, ".gitignore");
140 assert!(res.is_err());
141
142 let lines = read_lines(root.join(ignore_file_path)).unwrap();
143 assert_eq!(lines.count(), 1);
144 }
145}