Mountain/Environment/
FileSystemProvider.rs

1//! # FileSystemProvider Implementation
2//!
3//! Implements the `FileSystemReader` and `FileSystemWriter` traits for the
4//! `MountainEnvironment`, providing the concrete logic for all filesystem
5//! operations.
6
7#![allow(non_snake_case, non_camel_case_types)]
8
9use std::path::PathBuf;
10
11use Common::{
12	Error::CommonError::CommonError,
13	FileSystem::{
14		DTO::{FileSystemStatDTO::FileSystemStatDTO, FileTypeDTO::FileTypeDTO},
15		FileSystemReader::FileSystemReader,
16		FileSystemWriter::FileSystemWriter,
17	},
18};
19use async_trait::async_trait;
20use tokio::fs;
21
22use super::{MountainEnvironment::MountainEnvironment, Utility};
23
24#[async_trait]
25impl FileSystemReader for MountainEnvironment {
26	/// Reads the entire contents of a file into a byte vector after verifying
27	/// access rights.
28	async fn ReadFile(&self, Path:&PathBuf) -> Result<Vec<u8>, CommonError> {
29		Utility::IsPathAllowedForAccess(&self.ApplicationState, Path)?;
30
31		fs::read(Path)
32			.await
33			.map_err(|Error| CommonError::FromStandardIOError(Error, Path.clone(), "ReadFile"))
34	}
35
36	/// Retrieves metadata for a file or directory after verifying access
37	/// rights.
38	async fn StatFile(&self, Path:&PathBuf) -> Result<FileSystemStatDTO, CommonError> {
39		Utility::IsPathAllowedForAccess(&self.ApplicationState, Path)?;
40
41		let Metadata = fs::metadata(Path)
42			.await
43			.map_err(|Error| CommonError::FromStandardIOError(Error, Path.clone(), "StatFile"))?;
44
45		let mut FileType = 0_u8;
46
47		if Metadata.is_file() {
48			FileType |= FileTypeDTO::File as u8;
49		}
50
51		if Metadata.is_dir() {
52			FileType |= FileTypeDTO::Directory as u8;
53		}
54
55		if Metadata.file_type().is_symlink() {
56			FileType |= FileTypeDTO::SymbolicLink as u8;
57		}
58
59		let GetMilliTimestamp = |SystemTimeResult:Result<std::time::SystemTime, _>| -> u64 {
60			SystemTimeResult
61				.ok()
62				.and_then(|time| time.duration_since(std::time::SystemTime::UNIX_EPOCH).ok())
63				.map_or(0, |duration| duration.as_millis() as u64)
64		};
65
66		Ok(FileSystemStatDTO {
67			FileType,
68
69			CreationTime:GetMilliTimestamp(Metadata.created()),
70
71			ModificationTime:GetMilliTimestamp(Metadata.modified()),
72
73			Size:Metadata.len(),
74
75			// Permissions are not yet implemented.
76			Permissions:None,
77		})
78	}
79
80	/// Reads the contents of a directory after verifying access rights.
81	async fn ReadDirectory(&self, Path:&PathBuf) -> Result<Vec<(String, FileTypeDTO)>, CommonError> {
82		Utility::IsPathAllowedForAccess(&self.ApplicationState, Path)?;
83
84		let mut Entries = Vec::new();
85
86		let mut ReadDirectory = fs::read_dir(Path)
87			.await
88			.map_err(|Error| CommonError::FromStandardIOError(Error, Path.clone(), "ReadDirectory"))?;
89
90		while let Some(EntryResult) = ReadDirectory
91			.next_entry()
92			.await
93			.map_err(|Error| CommonError::FromStandardIOError(Error, Path.clone(), "ReadDirectory.NextEntry"))?
94		{
95			let FileName = EntryResult.file_name().to_string_lossy().into_owned();
96
97			let FileType = match EntryResult.file_type().await {
98				Ok(ft) => {
99					if ft.is_dir() {
100						FileTypeDTO::Directory
101					} else if ft.is_file() {
102						FileTypeDTO::File
103					} else {
104						FileTypeDTO::Unknown
105					}
106				},
107
108				Err(_) => FileTypeDTO::Unknown,
109			};
110
111			Entries.push((FileName, FileType));
112		}
113
114		Ok(Entries)
115	}
116}
117
118#[async_trait]
119impl FileSystemWriter for MountainEnvironment {
120	/// Writes content to a file after verifying access rights and options.
121	async fn WriteFile(&self, Path:&PathBuf, Content:Vec<u8>, Create:bool, Overwrite:bool) -> Result<(), CommonError> {
122		Utility::IsPathAllowedForAccess(&self.ApplicationState, Path)?;
123
124		let PathExists = fs::try_exists(Path).await.unwrap_or(false);
125
126		if PathExists && !Overwrite {
127			return Err(CommonError::FileSystemFileExists(Path.clone()));
128		}
129
130		if !PathExists && !Create {
131			return Err(CommonError::FileSystemNotFound(Path.clone()));
132		}
133
134		if let Some(ParentDirectory) = Path.parent() {
135			if !fs::try_exists(ParentDirectory).await.unwrap_or(false) {
136				fs::create_dir_all(ParentDirectory).await.map_err(|Error| {
137					CommonError::FromStandardIOError(Error, ParentDirectory.to_path_buf(), "WriteFile.CreateParent")
138				})?;
139			}
140		}
141
142		fs::write(Path, &Content)
143			.await
144			.map_err(|Error| CommonError::FromStandardIOError(Error, Path.clone(), "WriteFile"))
145	}
146
147	/// Creates a directory after verifying access rights.
148	async fn CreateDirectory(&self, Path:&PathBuf, Recursive:bool) -> Result<(), CommonError> {
149		Utility::IsPathAllowedForAccess(&self.ApplicationState, Path)?;
150
151		let Operation = if Recursive {
152			fs::create_dir_all(Path).await
153		} else {
154			fs::create_dir(Path).await
155		};
156
157		Operation.map_err(|Error| CommonError::FromStandardIOError(Error, Path.clone(), "CreateDirectory"))
158	}
159
160	/// Deletes a file or directory after verifying access rights.
161	async fn Delete(&self, Path:&PathBuf, Recursive:bool, _UseTrash:bool) -> Result<(), CommonError> {
162		Utility::IsPathAllowedForAccess(&self.ApplicationState, Path)?;
163
164		// A full implementation would use the `trash` crate if `UseTrash` is true.
165		match fs::metadata(Path).await {
166			Ok(Metadata) => {
167				let Operation = if Metadata.is_dir() {
168					if Recursive {
169						fs::remove_dir_all(Path).await
170					} else {
171						fs::remove_dir(Path).await
172					}
173				} else {
174					fs::remove_file(Path).await
175				};
176
177				Operation.map_err(|Error| CommonError::FromStandardIOError(Error, Path.clone(), "Delete"))
178			},
179
180			// Idempotent success
181			Err(Error) if Error.kind() == std::io::ErrorKind::NotFound => Ok(()),
182
183			Err(Error) => Err(CommonError::FromStandardIOError(Error, Path.clone(), "Delete.Stat")),
184		}
185	}
186
187	/// Renames (moves) a file or directory after verifying access rights.
188	async fn Rename(&self, Source:&PathBuf, Target:&PathBuf, Overwrite:bool) -> Result<(), CommonError> {
189		Utility::IsPathAllowedForAccess(&self.ApplicationState, Source)?;
190
191		Utility::IsPathAllowedForAccess(&self.ApplicationState, Target)?;
192
193		if !Overwrite && fs::try_exists(Target).await.unwrap_or(false) {
194			return Err(CommonError::FileSystemFileExists(Target.clone()));
195		}
196
197		fs::rename(Source, Target)
198			.await
199			.map_err(|Error| CommonError::FromStandardIOError(Error, Source.clone(), "Rename"))
200	}
201
202	/// Copies a file after verifying access rights.
203	async fn Copy(&self, Source:&PathBuf, Target:&PathBuf, Overwrite:bool) -> Result<(), CommonError> {
204		Utility::IsPathAllowedForAccess(&self.ApplicationState, Source)?;
205
206		Utility::IsPathAllowedForAccess(&self.ApplicationState, Target)?;
207
208		let SourceMetadata = self.StatFile(Source).await?;
209
210		if (SourceMetadata.FileType & FileTypeDTO::Directory as u8) != 0 {
211			return Err(CommonError::NotImplemented { FeatureName:"Recursive directory copy".to_string() });
212		}
213
214		if !Overwrite && fs::try_exists(Target).await.unwrap_or(false) {
215			return Err(CommonError::FileSystemFileExists(Target.clone()));
216		}
217
218		fs::copy(Source, Target)
219			.await
220			.map(|_| ())
221			.map_err(|Error| CommonError::FromStandardIOError(Error, Source.clone(), "Copy"))
222	}
223
224	/// Creates a new, empty file after verifying access rights.
225	async fn CreateFile(&self, Path:&PathBuf) -> Result<(), CommonError> {
226		// Use WriteFile with an empty Vec, ensuring creation without overwrite.
227		self.WriteFile(Path, vec![], true, false).await
228	}
229}