1use crate::project::buildable::{Buildable, BuiltByContainer, IntoBuildable};
2use std::fmt::{Debug, Display, Formatter};
3use std::fs::{File, Metadata, OpenOptions};
4use std::io;
5use std::io::{Read, Write};
6
7use std::path::{Path, PathBuf};
8
9pub struct RegularFile {
11 path: PathBuf,
12 file: File,
13 open_options: OpenOptions,
14 built_by: BuiltByContainer,
15}
16
17assert_impl_all!(RegularFile: Send, Sync);
18
19impl RegularFile {
20 pub fn with_options<P: AsRef<Path>>(path: P, options: &OpenOptions) -> io::Result<Self> {
22 Ok(Self {
23 path: path.as_ref().to_path_buf(),
24 file: options.open(path)?,
25 open_options: options.clone(),
26 built_by: BuiltByContainer::default(),
27 })
28 }
29
30 pub fn create<P: AsRef<Path>>(path: P) -> io::Result<Self> {
34 Self::with_options(
35 path,
36 File::options().create(true).write(true).truncate(true),
37 )
38 }
39
40 pub fn open<P: AsRef<Path>>(path: P) -> io::Result<Self> {
42 Self::with_options(path, File::options().read(true))
43 }
44
45 pub fn path(&self) -> &Path {
47 &self.path
48 }
49
50 pub fn built_by<T: Buildable + 'static>(&mut self, task: T) {
52 self.built_by.add(task)
53 }
54
55 pub fn file(&self) -> &File {
57 &self.file
58 }
59
60 pub fn metadata(&self) -> io::Result<Metadata> {
61 self.file().metadata()
62 }
63}
64
65impl Debug for RegularFile {
66 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
67 f.debug_struct("RegularFile")
68 .field("path", &self.path)
69 .field("open_options", &self.open_options)
70 .field("built_buy", &"...")
71 .finish()
72 }
73}
74
75impl From<RegularFile> for PathBuf {
76 fn from(rf: RegularFile) -> Self {
77 rf.path
78 }
79}
80
81impl Display for RegularFile {
82 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
83 write!(f, "{:?}", self.path)
84 }
85}
86
87impl Read for RegularFile {
88 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
89 self.file.read(buf)
90 }
91}
92
93impl Read for &RegularFile {
94 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
95 (&self.file).read(buf)
96 }
97}
98
99impl Write for RegularFile {
100 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
101 self.file.write(buf)
102 }
103
104 fn flush(&mut self) -> io::Result<()> {
105 self.file.flush()
106 }
107}
108
109impl AsRef<Path> for RegularFile {
110 fn as_ref(&self) -> &Path {
111 &self.path
112 }
113}
114
115impl IntoBuildable for &RegularFile {
116 type Buildable = BuiltByContainer;
117
118 fn into_buildable(self) -> Self::Buildable {
119 self.built_by.clone()
120 }
121}
122
123pub trait AsFileLocation {
125 type FilePath: AsRef<Path>;
127
128 fn file_location(&self) -> Self::FilePath;
130}
131
132impl<P: AsRef<Path>> AsFileLocation for P {
133 type FilePath = PathBuf;
134
135 fn file_location(&self) -> Self::FilePath {
136 self.as_ref().to_path_buf()
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use super::*;
143 use std::io::Read;
144 use std::io::Write;
145 use tempfile::TempDir;
146
147 #[test]
148 fn create_file() {
149 let tempdir = TempDir::new().unwrap();
150 let file = RegularFile::with_options(
151 tempdir.path().join("file"),
152 OpenOptions::new().create(true).write(true),
153 )
154 .unwrap();
155
156 assert_eq!(file.path(), tempdir.path().join("file"));
157 }
158
159 #[test]
160 fn can_write() {
161 let tempdir = TempDir::new().unwrap();
162 let file = RegularFile::with_options(
163 tempdir.path().join("file"),
164 OpenOptions::new().create(true).write(true),
165 )
166 .unwrap();
167
168 writeln!(file.file(), "Hello, World!").expect("Couldn't write to file");
169 }
170
171 #[test]
172 fn can_read() {
173 let tempdir = TempDir::new().unwrap();
174 let reg_file = RegularFile::with_options(
175 tempdir.path().join("file"),
176 OpenOptions::new().create(true).write(true),
177 )
178 .unwrap();
179
180 let mut file = reg_file.file();
181 writeln!(file, "Hello, World!").expect("Couldn't write to file");
182
183 let mut file =
184 RegularFile::with_options(tempdir.path().join("file"), OpenOptions::new().read(true))
185 .unwrap();
186
187 let mut buffer = String::new();
188 file.read_to_string(&mut buffer)
189 .expect("Couldn't read from file");
190 assert_eq!(buffer.trim(), "Hello, World!");
191 }
192}