gveditor_core_api/filesystems/
local.rs1use async_trait::async_trait;
2use tokio::fs;
3use tokio_stream::wrappers::ReadDirStream;
4use tokio_stream::StreamExt;
5
6use crate::Errors;
7
8use super::{DirItemInfo, FileInfo, Filesystem, FilesystemErrors};
9use std::io::ErrorKind;
10
11#[derive(Default)]
13pub struct LocalFilesystem;
14
15impl LocalFilesystem {
16 pub fn new() -> Self {
17 Self
18 }
19}
20
21#[async_trait]
22impl Filesystem for LocalFilesystem {
23 async fn read_file_by_path(&self, path: &str) -> Result<FileInfo, Errors> {
25 fs::read_to_string(path)
26 .await
27 .map(|content| FileInfo::new(path, content))
28 .map_err(|err| match err.kind() {
29 ErrorKind::NotFound => Errors::Fs(FilesystemErrors::FileNotFound),
30 _ => Errors::Fs(FilesystemErrors::FileNotFound),
31 })
32 }
33
34 async fn write_file_by_path(&self, path: &str, content: &str) -> Result<(), Errors> {
36 fs::write(path, content)
37 .await
38 .map_err(|err| match err.kind() {
39 ErrorKind::NotFound => Errors::Fs(FilesystemErrors::FileNotFound),
40 _ => Errors::Fs(FilesystemErrors::FileNotFound),
41 })
42 }
43
44 async fn list_dir_by_path(&self, path: &str) -> Result<Vec<DirItemInfo>, Errors> {
46 let dirs = fs::read_dir(path).await;
47
48 if let Ok(dirs) = dirs {
49 let mut result = Vec::new();
50 let mut items = ReadDirStream::new(dirs);
51
52 while let Some(Ok(item)) = items.next().await {
54 let path = item.path();
55 let str_path = path.as_os_str().to_str().unwrap().to_string();
56 let item_name = path.file_name().unwrap().to_str().unwrap().to_string();
57 let is_file = path.is_file();
58 result.push(DirItemInfo {
59 path: str_path,
60 name: item_name,
61 is_file,
62 });
63 }
64
65 result.sort_by_key(|item| item.is_file);
66
67 Ok(result)
68 } else {
69 let err = dirs.unwrap_err();
70 Err(match err.kind() {
71 ErrorKind::NotFound => Errors::Fs(FilesystemErrors::FileNotFound),
72 _ => Errors::Fs(FilesystemErrors::FileNotFound),
73 })
74 }
75 }
76}
77
78#[cfg(test)]
79mod tests {
80
81 use super::{Filesystem, LocalFilesystem};
82
83 #[tokio::test]
84 async fn read_files() {
85 let fs = LocalFilesystem::new();
86
87 let file_exists = fs.read_file_by_path("../readme.md").await.is_ok();
88 let doesnt_exist = fs.read_file_by_path("rust_>_*").await.is_err();
89
90 assert!(file_exists);
91 assert!(doesnt_exist);
92 }
93
94 #[tokio::test]
95 async fn list_dir() {
96 let fs = LocalFilesystem::new();
97
98 let items_in_dir = fs.list_dir_by_path(".").await;
99
100 assert!(items_in_dir.is_ok());
101
102 let items_in_dir = items_in_dir.unwrap();
103
104 assert!(items_in_dir.len() > 1);
105
106 assert!(!items_in_dir[0].is_file);
107 }
108}