1use std::{
2 self,
3 ffi::OsStr,
4 fs::DirEntry,
5 io::{
6 self,
7 Read,
8 },
9 ops::ControlFlow,
10 path::{
11 Path,
12 PathBuf,
13 },
14};
15
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
19pub enum DirectoryEntry {
20 Directory(DirectoryInfo),
22
23 File(FileInfo),
25}
26
27impl DirectoryEntry {
28 pub fn name(&self) -> &str {
29 match self {
30 Self::Directory(dir) => &dir.directory_name,
31 Self::File(file) => &file.file_name,
32 }
33 }
34}
35
36impl From<FileInfo> for DirectoryEntry {
37 fn from(value: FileInfo) -> Self {
38 Self::File(value)
39 }
40}
41
42impl From<DirectoryInfo> for DirectoryEntry {
43 fn from(value: DirectoryInfo) -> Self {
44 Self::Directory(value)
45 }
46}
47
48impl TryFrom<DirEntry> for DirectoryEntry {
49 type Error = std::io::Error;
50
51 fn try_from(value: DirEntry) -> Result<Self, Self::Error> {
52 use std::os::windows::fs::MetadataExt;
53
54 let file_name = value.file_name().to_string_lossy().to_string();
55 let file_type = value.file_type()?;
56 let metadata = value.metadata()?;
57 if file_type.is_dir() {
58 Ok(DirectoryInfo {
59 directory_name: file_name,
60 directory_attributes: metadata.file_attributes(),
61
62 creation_time: metadata.creation_time(),
63 last_access_time: metadata.last_access_time(),
64 last_write_time: metadata.last_write_time(),
65 }
66 .into())
67 } else if file_type.is_file() {
68 Ok(FileInfo {
69 file_name,
70 file_size: metadata.len(),
71 file_attributes: metadata.file_attributes(),
72
73 creation_time: metadata.creation_time(),
74 last_access_time: metadata.last_access_time(),
75 last_write_time: metadata.last_write_time(),
76 }
77 .into())
78 } else {
79 Err(io::Error::other("file type is not supported"))
80 }
81 }
82}
83
84#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
90pub struct FileInfo {
91 pub file_name: String,
92 pub file_size: u64,
93 pub file_attributes: u32,
94
95 pub creation_time: u64,
96 pub last_access_time: u64,
97 pub last_write_time: u64,
98}
99
100#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
102pub struct DirectoryInfo {
103 pub directory_name: String,
104 pub directory_attributes: u32,
105
106 pub creation_time: u64,
107 pub last_access_time: u64,
108 pub last_write_time: u64,
109}
110
111pub trait ProjectedFileSystemSource {
113 fn list_directory(&self, path: &Path) -> Vec<DirectoryEntry>;
116
117 fn get_directory_entry(&self, path: &Path) -> Option<DirectoryEntry> {
126 let directory = path.parent().map(Path::to_path_buf).unwrap_or_default();
127 let file_name = path.file_name().map(OsStr::to_string_lossy)?;
128
129 self.list_directory(&directory)
130 .into_iter()
131 .find(|entry| entry.name() == file_name)
132 }
133
134 fn stream_file_content(
140 &self,
141 path: &Path,
142 byte_offset: usize,
143 length: usize,
144 ) -> std::io::Result<Box<dyn Read>>;
145
146 fn handle_notification(&self, _notification: &Notification) -> ControlFlow<()> {
149 ControlFlow::Continue(())
150 }
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
154pub enum FileCloseAction {
155 Deleted,
157
158 Modified,
160
161 NoModification,
163}
164
165#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
166pub struct ProjectedFile {
167 pub file_id: u128,
168 pub is_directory: bool,
169 pub path: PathBuf,
170}
171
172#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
173pub struct FileRenameInfo {
174 pub source: Option<PathBuf>,
175 pub destination: Option<PathBuf>,
176}
177
178#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
179pub enum Notification {
180 FileCreated(ProjectedFile),
181 FileOpened(ProjectedFile),
182 FileClosed(ProjectedFile, FileCloseAction),
183 FileOverwritten(ProjectedFile),
184
185 PreFileRename(FileRenameInfo),
186 FileRenamed(FileRenameInfo),
187
188 PreSetHardlink(ProjectedFile),
189 HardlinkCreated(ProjectedFile),
190
191 PreFileDelete(ProjectedFile),
192 FilePreConvertToFull(ProjectedFile),
193}
194
195impl Notification {
196 pub fn is_cancelable(&self) -> bool {
199 #[allow(clippy::match_like_matches_macro)]
200 match self {
201 Self::PreFileRename(_) => true,
202 Self::PreFileDelete(_) => true,
203 Self::PreSetHardlink(_) => true,
204 Self::FilePreConvertToFull(_) => true,
205 _ => false,
206 }
207 }
208}