1use eyre::Context;
2use eyre::Result;
3
4use std::{
5 fs::{self, DirEntry},
6 path::Path,
7};
8
9use crate::models::FileInfo;
10use crate::models::SimpleFileKind;
11
12pub trait FileSystem {
13 fn current_directory(&self) -> Result<FileInfo>;
14
15 fn list_files(&self, file: &FileInfo) -> Result<Vec<FileInfo>>;
16
17 fn file_size(&self, file: &FileInfo) -> Result<u64>;
18}
19
20pub trait FileSystemClean {
21 fn remove_file(&self, file: &FileInfo) -> Result<()>;
22}
23pub struct RealFileSystem;
24
25impl FileSystem for RealFileSystem {
26 fn current_directory(&self) -> Result<FileInfo> {
27 let path_buf = std::env::current_dir()?;
28 Ok(FileInfo::new(
29 path_buf,
30 "".into(),
31 SimpleFileKind::Directory,
32 ))
33 }
34
35 fn list_files(&self, file: &FileInfo) -> Result<Vec<FileInfo>> {
36 fs::read_dir(&file.path)?
37 .map(|e| {
38 e.context("failed to read dir entry")
39 .and_then(|entry| map_entry_to_simple_file(&entry))
40 })
41 .collect()
42 }
43
44 fn file_size(&self, file: &FileInfo) -> Result<u64> {
45 RealFileSystem::get_size(&file.path)
46 }
47}
48
49impl RealFileSystem {
50 pub fn get_size<P>(path: P) -> Result<u64>
51 where
52 P: AsRef<Path>,
53 {
54 let mut result = 0;
55
56 if path.as_ref().is_dir() {
57 for entry in fs::read_dir(&path)? {
58 let current_path = entry?.path();
59 if current_path.is_file() {
60 result += current_path.metadata()?.len();
61 } else {
62 result += RealFileSystem::get_size(current_path)?;
63 }
64 }
65 } else {
66 result = path.as_ref().metadata()?.len();
67 }
68 Ok(result)
69 }
70}
71
72fn map_entry_to_simple_file(entry: &DirEntry) -> Result<FileInfo> {
73 let path = entry.path();
74
75 let name = entry
76 .file_name()
77 .into_string()
78 .map_err(|_| eyre::eyre!("Cannot convert os string"))?;
79
80 let file_type = entry.file_type()?;
81
82 let kind = if file_type.is_dir() {
83 SimpleFileKind::Directory
84 } else {
85 SimpleFileKind::File
86 };
87
88 Ok(FileInfo::new(path, name, kind))
89}
90
91impl FileSystemClean for RealFileSystem {
92 fn remove_file(&self, file: &FileInfo) -> Result<()> {
93 if file.kind == SimpleFileKind::Directory {
94 std::fs::remove_dir_all(&file.path)?;
95 } else {
96 std::fs::remove_file(&file.path)?;
97 }
98 Ok(())
99 }
100}
101
102pub struct MockFileSystemClean;
103
104impl FileSystemClean for MockFileSystemClean {
105 fn remove_file(&self, _file: &FileInfo) -> Result<()> {
106 std::thread::sleep(std::time::Duration::from_secs(1));
107 Ok(())
108 }
110}