Mountain/Environment/FileSystemProvider/
ReadOperations.rs1use std::path::PathBuf;
23
24use CommonLibrary::{
25 Error::CommonError::CommonError,
26 FileSystem::DTO::{FileSystemStatDTO::FileSystemStatDTO, FileTypeDTO::FileTypeDTO},
27};
28use tokio::fs;
29
30use super::super::{MountainEnvironment::MountainEnvironment, Utility};
31
32pub(super) async fn read_file_impl(env:&MountainEnvironment, path:&PathBuf) -> Result<Vec<u8>, CommonError> {
34 Utility::PathSecurity::IsPathAllowedForAccess(&env.ApplicationState, path)?;
35
36 let metadata = fs::metadata(path)
38 .await
39 .map_err(|error| CommonError::FromStandardIOError(error, path.clone(), "ReadFile.Stat"))?;
40
41 if metadata.is_dir() {
42 return Err(CommonError::InvalidArgument {
43 ArgumentName:"Path".to_string(),
44 Reason:format!("Cannot read directory as file: {}", path.display()),
45 });
46 }
47
48 fs::read(path)
49 .await
50 .map_err(|error| CommonError::FromStandardIOError(error, path.clone(), "ReadFile"))
51}
52
53pub(super) async fn stat_file_impl(env:&MountainEnvironment, path:&PathBuf) -> Result<FileSystemStatDTO, CommonError> {
55 Utility::PathSecurity::IsPathAllowedForAccess(&env.ApplicationState, path)?;
56
57 let metadata = fs::metadata(path)
58 .await
59 .map_err(|error| CommonError::FromStandardIOError(error, path.clone(), "StatFile"))?;
60
61 let mut file_type = 0_u8;
62
63 if metadata.is_file() {
64 file_type |= FileTypeDTO::File as u8;
65 }
66
67 if metadata.is_dir() {
68 file_type |= FileTypeDTO::Directory as u8;
69 }
70
71 let file_type_raw = fs::symlink_metadata(path)
73 .await
74 .map_err(|error| CommonError::FromStandardIOError(error, path.clone(), "StatFile.FileType"))?;
75
76 if file_type_raw.is_symlink() {
77 file_type |= FileTypeDTO::SymbolicLink as u8;
78 }
79
80 let get_milli_timestamp = |system_time_result:Result<std::time::SystemTime, _>| -> u64 {
82 system_time_result
83 .ok()
84 .and_then(|time| time.duration_since(std::time::SystemTime::UNIX_EPOCH).ok())
85 .map_or(0, |duration| duration.as_millis() as u64)
86 };
87
88 Ok(FileSystemStatDTO {
89 FileType:file_type,
90
91 CreationTime:get_milli_timestamp(metadata.created()),
92
93 ModificationTime:get_milli_timestamp(metadata.modified()),
94
95 Size:metadata.len(),
96
97 Permissions:None,
104 })
105}
106
107pub(super) async fn read_directory_impl(
109 env:&MountainEnvironment,
110
111 path:&PathBuf,
112) -> Result<Vec<(String, FileTypeDTO)>, CommonError> {
113 Utility::PathSecurity::IsPathAllowedForAccess(&env.ApplicationState, path)?;
114
115 let metadata = fs::metadata(path)
117 .await
118 .map_err(|error| CommonError::FromStandardIOError(error, path.clone(), "ReadDirectory.Stat"))?;
119
120 if !metadata.is_dir() {
121 return Err(CommonError::InvalidArgument {
122 ArgumentName:"Path".to_string(),
123 Reason:format!("Cannot read directory: path is not a directory: {}", path.display()),
124 });
125 }
126
127 let mut entries = Vec::new();
128
129 let mut read_dir = fs::read_dir(path)
130 .await
131 .map_err(|error| CommonError::FromStandardIOError(error, path.clone(), "ReadDirectory"))?;
132
133 while let Some(entry_result) = read_dir
134 .next_entry()
135 .await
136 .map_err(|error| CommonError::FromStandardIOError(error, path.clone(), "ReadDirectory.NextEntry"))?
137 {
138 let file_name = entry_result.file_name().to_string_lossy().into_owned();
139
140 let file_type = match entry_result.file_type().await {
142 Ok(ft) => {
143 if ft.is_symlink() {
144 FileTypeDTO::SymbolicLink
145 } else if ft.is_dir() {
146 FileTypeDTO::Directory
147 } else if ft.is_file() {
148 FileTypeDTO::File
149 } else {
150 FileTypeDTO::Unknown
151 }
152 },
153
154 Err(_) => FileTypeDTO::Unknown,
155 };
156
157 entries.push((file_name, file_type));
158 }
159
160 Ok(entries)
161}