1use std::path::{Path, PathBuf};
7
8#[derive(Debug, Clone, PartialEq)]
15pub enum FileData {
16 Bytes(Vec<u8>),
18 Path(PathBuf),
21}
22
23impl FileData {
24 pub fn write_to(&self, dest: &Path) -> Result<(), std::io::Error> {
30 match self {
31 FileData::Bytes(data) => std::fs::write(dest, data),
32 FileData::Path(src) => {
33 match std::fs::rename(src, dest) {
35 Ok(()) => Ok(()),
36 Err(_) => {
37 std::fs::copy(src, dest)?;
39 let _ = std::fs::remove_file(src);
40 Ok(())
41 }
42 }
43 }
44 }
45 }
46
47 pub fn len(&self) -> Result<u64, std::io::Error> {
50 match self {
51 FileData::Bytes(data) => Ok(data.len() as u64),
52 FileData::Path(path) => std::fs::metadata(path).map(|m| m.len()),
53 }
54 }
55
56 pub fn is_empty(&self) -> Result<bool, std::io::Error> {
58 self.len().map(|n| n == 0)
59 }
60
61 pub fn copy_to(&self, dest: &Path) -> Result<(), std::io::Error> {
66 match self {
67 FileData::Bytes(data) => std::fs::write(dest, data),
68 FileData::Path(src) => {
69 std::fs::copy(src, dest)?;
70 Ok(())
71 }
72 }
73 }
74
75 pub fn into_bytes(self) -> Result<Vec<u8>, std::io::Error> {
78 match self {
79 FileData::Bytes(data) => Ok(data),
80 FileData::Path(path) => std::fs::read(path),
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_filedata_bytes_write_to() {
91 let dir = std::env::temp_dir().join("bnto-test-filedata-bytes");
92 let _ = std::fs::remove_dir_all(&dir);
93 std::fs::create_dir_all(&dir).unwrap();
94
95 let data = FileData::Bytes(b"hello world".to_vec());
96 let dest = dir.join("output.txt");
97 data.write_to(&dest).unwrap();
98
99 assert_eq!(std::fs::read(&dest).unwrap(), b"hello world");
100 let _ = std::fs::remove_dir_all(&dir);
101 }
102
103 #[test]
104 fn test_filedata_path_write_to_renames() {
105 let dir = std::env::temp_dir().join("bnto-test-filedata-path");
106 let _ = std::fs::remove_dir_all(&dir);
107 std::fs::create_dir_all(&dir).unwrap();
108
109 let src = dir.join("source.bin");
110 std::fs::write(&src, b"file content").unwrap();
111
112 let data = FileData::Path(src.clone());
113 let dest = dir.join("moved.bin");
114 data.write_to(&dest).unwrap();
115
116 assert_eq!(std::fs::read(&dest).unwrap(), b"file content");
117 assert!(!src.exists());
119 let _ = std::fs::remove_dir_all(&dir);
120 }
121
122 #[test]
123 fn test_filedata_bytes_len() {
124 let data = FileData::Bytes(vec![0u8; 42]);
125 assert_eq!(data.len().unwrap(), 42);
126 }
127
128 #[test]
129 fn test_filedata_path_len() {
130 let dir = std::env::temp_dir().join("bnto-test-filedata-len");
131 let _ = std::fs::remove_dir_all(&dir);
132 std::fs::create_dir_all(&dir).unwrap();
133
134 let path = dir.join("sized.bin");
135 std::fs::write(&path, vec![0u8; 1024]).unwrap();
136
137 let data = FileData::Path(path);
138 assert_eq!(data.len().unwrap(), 1024);
139 let _ = std::fs::remove_dir_all(&dir);
140 }
141
142 #[test]
143 fn test_filedata_bytes_is_empty() {
144 assert!(FileData::Bytes(vec![]).is_empty().unwrap());
145 assert!(!FileData::Bytes(vec![1]).is_empty().unwrap());
146 }
147
148 #[test]
149 fn test_filedata_bytes_copy_to() {
150 let dir = std::env::temp_dir().join("bnto-test-filedata-bytes-copy");
151 let _ = std::fs::remove_dir_all(&dir);
152 std::fs::create_dir_all(&dir).unwrap();
153
154 let data = FileData::Bytes(b"copy me".to_vec());
155 let dest = dir.join("copied.txt");
156 data.copy_to(&dest).unwrap();
157
158 assert_eq!(std::fs::read(&dest).unwrap(), b"copy me");
159 let _ = std::fs::remove_dir_all(&dir);
160 }
161
162 #[test]
163 fn test_filedata_path_copy_to_preserves_source() {
164 let dir = std::env::temp_dir().join("bnto-test-filedata-path-copy");
165 let _ = std::fs::remove_dir_all(&dir);
166 std::fs::create_dir_all(&dir).unwrap();
167
168 let src = dir.join("source.bin");
169 std::fs::write(&src, b"original content").unwrap();
170
171 let data = FileData::Path(src.clone());
172 let dest = dir.join("copied.bin");
173 data.copy_to(&dest).unwrap();
174
175 assert_eq!(std::fs::read(&dest).unwrap(), b"original content");
176 assert!(src.exists(), "Source should still exist after copy_to");
177 let _ = std::fs::remove_dir_all(&dir);
178 }
179
180 #[test]
181 fn test_filedata_bytes_into_bytes() {
182 let data = FileData::Bytes(b"content".to_vec());
183 assert_eq!(data.into_bytes().unwrap(), b"content");
184 }
185
186 #[test]
187 fn test_filedata_path_into_bytes() {
188 let dir = std::env::temp_dir().join("bnto-test-filedata-into");
189 let _ = std::fs::remove_dir_all(&dir);
190 std::fs::create_dir_all(&dir).unwrap();
191
192 let path = dir.join("read-me.bin");
193 std::fs::write(&path, b"disk content").unwrap();
194
195 let data = FileData::Path(path);
196 assert_eq!(data.into_bytes().unwrap(), b"disk content");
197 let _ = std::fs::remove_dir_all(&dir);
198 }
199}