1use crate::{VersionError, VersionResult};
2use regex::Regex;
3use serde::{Deserialize, Serialize};
4use std::{
5 env,
6 fs::{remove_file, rename, File, OpenOptions},
7 io::{BufRead, BufReader, BufWriter, Lines, Write},
8 path::PathBuf,
9};
10
11#[derive(Deserialize, Serialize, Clone)]
12pub struct TrackedFiles {
13 pub file: String,
14 pub expr: String,
15}
16
17impl TrackedFiles {
18 pub fn new(file: String, expr: String) -> Self {
19 TrackedFiles { file, expr }
20 }
21
22 pub fn new_from_path(file: PathBuf, expr: String) -> Self {
23 TrackedFiles {
24 file: file.to_string_lossy().to_string(),
25 expr,
26 }
27 }
28
29 pub fn new_from_re(file: String, expr: Regex) -> Self {
30 TrackedFiles {
31 file,
32 expr: expr.as_str().to_string(),
33 }
34 }
35
36 pub fn new_from_path_and_regex(file: PathBuf, expr: Regex) -> Self {
37 TrackedFiles {
38 file: file.to_string_lossy().to_string(),
39 expr: expr.as_str().to_string(),
40 }
41 }
42
43 pub fn open(&mut self) -> VersionResult<File> {
44 let cd = match env::current_dir() {
45 Ok(cd) => cd,
46 Err(e) => return Err(VersionError::IoError(e)),
47 };
48 let path = cd.join(&self.file);
49 match OpenOptions::new().read(true).open(&path) {
50 Ok(file) => Ok(file),
51 Err(e) => Err(VersionError::IoError(e)),
52 }
53 }
54
55 pub fn open_tmp(&mut self) -> VersionResult<File> {
56 let cd = match env::current_dir() {
57 Ok(cd) => cd,
58 Err(e) => return Err(VersionError::IoError(e)),
59 };
60 let mut path = cd.join(&self.file);
61 path = path.with_extension("tmp");
62 match OpenOptions::new().create(true).write(true).open(&path) {
63 Ok(file) => Ok(file),
64 Err(e) => Err(VersionError::IoError(e)),
65 }
66 }
67
68 pub fn close(&mut self) -> VersionResult<()> {
69 let cd = match env::current_dir() {
70 Ok(cd) => cd,
71 Err(e) => return Err(VersionError::IoError(e)),
72 };
73 let old_path = cd.join(&self.file);
74 let new_path = old_path.with_extension("tmp");
75 match remove_file(&old_path) {
76 Ok(_) => (),
77 Err(e) => return Err(VersionError::IoError(e)),
78 };
79 match rename(new_path, old_path) {
80 Ok(_) => Ok(()),
81 Err(e) => Err(VersionError::IoError(e)),
82 }
83 }
84
85 pub fn read_lines(&mut self) -> VersionResult<Lines<BufReader<File>>> {
86 let file = self.open()?;
87 let reader = BufReader::new(file);
88 Ok(reader.lines())
89 }
90
91 pub fn writer(&mut self) -> VersionResult<BufWriter<File>> {
92 let file = self.open_tmp()?;
93 Ok(BufWriter::new(file))
94 }
95
96 pub fn update(&mut self, version: String) -> VersionResult<()> {
97 let mut writer = self.writer()?;
98 let regex = match Regex::new(&self.expr) {
99 Ok(re) => re,
100 Err(e) => return Err(VersionError::RegexError(e)),
101 };
102 let mut updated = false;
103 for line in match self.read_lines() {
104 Ok(lines) => lines,
105 Err(e) => return Err(e),
106 } {
107 let line = match line {
108 Ok(line) => format!("{}\n", line),
109 Err(e) => return Err(VersionError::IoError(e)),
110 };
111 if !updated {
112 if let Some(matches) = regex.captures(&line) {
113 let new = line.replace(&matches[1], version.as_str());
114 match writer.write(new.as_bytes()) {
115 Ok(_) => (),
116 Err(e) => return Err(VersionError::IoError(e)),
117 };
118 updated = true;
119 } else {
120 match writer.write(line.as_bytes()) {
121 Ok(_) => (),
122 Err(e) => return Err(VersionError::IoError(e)),
123 };
124 }
125 } else {
126 match writer.write(line.as_bytes()) {
127 Ok(_) => (),
128 Err(e) => return Err(VersionError::IoError(e)),
129 };
130 }
131 }
132 match writer.flush() {
133 Ok(_) => (),
134 Err(e) => return Err(VersionError::IoError(e)),
135 };
136 drop(writer);
137 self.close()
138 }
139}
140
141impl PartialEq<String> for TrackedFiles {
142 fn eq(&self, other: &String) -> bool {
143 self.file == *other
144 }
145}
146
147impl PartialEq<PathBuf> for TrackedFiles {
148 fn eq(&self, other: &PathBuf) -> bool {
149 let path = PathBuf::from(self.file.clone());
150 path == *other
151 }
152}
153
154impl PartialEq<String> for &TrackedFiles {
155 fn eq(&self, other: &String) -> bool {
156 self.file == *other
157 }
158}
159
160impl PartialEq<PathBuf> for &TrackedFiles {
161 fn eq(&self, other: &PathBuf) -> bool {
162 let path = PathBuf::from(self.file.clone());
163 path == *other
164 }
165}
166
167impl PartialEq<String> for &mut TrackedFiles {
168 fn eq(&self, other: &String) -> bool {
169 self.file == *other
170 }
171}
172
173impl PartialEq<PathBuf> for &mut TrackedFiles {
174 fn eq(&self, other: &PathBuf) -> bool {
175 let path = PathBuf::from(self.file.clone());
176 path == *other
177 }
178}