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