version_manager/
files.rs

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}