ready_set_rust/
gitignore.rs1use crate::templates::{GITIGNORE, GITIGNORE_BEGIN, GITIGNORE_END};
4
5#[must_use]
11pub fn plan(current: Option<&str>) -> Option<String> {
12 let desired = render();
13 match current {
14 None => Some(desired),
15 Some(text) if text == desired => None,
16 Some(text) => Some(merge_managed(text, &desired)),
17 }
18}
19
20fn render() -> String {
21 let mut out = String::new();
22 out.push_str(GITIGNORE_BEGIN);
23 out.push('\n');
24 out.push_str(GITIGNORE.trim_end());
25 out.push('\n');
26 out.push_str(GITIGNORE_END);
27 out.push('\n');
28 out
29}
30
31fn merge_managed(existing: &str, managed_block: &str) -> String {
32 let begin = format!("{GITIGNORE_BEGIN}\n");
33 let end = format!("{GITIGNORE_END}\n");
34
35 if let (Some(b), Some(e)) = (existing.find(&begin), existing.find(&end))
36 && b < e
37 {
38 let mut out = String::with_capacity(existing.len() + managed_block.len());
39 out.push_str(&existing[..b]);
40 out.push_str(managed_block);
41 let after = e + end.len();
42 if after < existing.len() {
43 out.push_str(&existing[after..]);
44 }
45 return out;
46 }
47
48 let mut out = String::with_capacity(existing.len() + managed_block.len() + 1);
49 out.push_str(existing);
50 if !existing.ends_with('\n') {
51 out.push('\n');
52 }
53 out.push_str(managed_block);
54 out
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 #[test]
62 fn matching_content_is_a_noop() {
63 let target = render();
64 assert!(plan(Some(&target)).is_none());
65 }
66}