floppy_disk/
lib.rs

1//! DIY: `#[derive(Clone, Debug)]`
2
3use std::ffi::OsString;
4use std::fmt::Debug;
5use std::io::Result;
6use std::path::{Path, PathBuf};
7use std::time::SystemTime;
8
9use tokio::io::{AsyncRead, AsyncSeek, AsyncWrite};
10
11pub mod mem;
12pub mod tokio_fs;
13
14pub mod prelude {
15    pub use crate::{
16        FloppyDirBuilder, FloppyDirEntry, FloppyDisk, FloppyDiskUnixExt, FloppyFile,
17        FloppyFileType, FloppyMetadata, FloppyOpenOptions, FloppyPermissions, FloppyReadDir,
18        FloppyUnixMetadata, FloppyUnixPermissions,
19    };
20
21    pub use crate::mem::MemFloppyDisk;
22    pub use crate::tokio_fs::TokioFloppyDisk;
23}
24
25#[async_trait::async_trait]
26pub trait FloppyDisk<'a>: Debug + std::marker::Unpin + std::marker::Sized + Send {
27    type DirBuilder: FloppyDirBuilder + Send + 'a;
28    type DirEntry: FloppyDirEntry<'a, Self> + Send + 'a;
29    type File: FloppyFile<'a, Self> + Send + 'a;
30    type FileType: FloppyFileType + Send + 'a;
31    type Metadata: FloppyMetadata<'a, Self> + Send + 'a;
32    type OpenOptions: FloppyOpenOptions<'a, Self> + Send + 'a;
33    type Permissions: FloppyPermissions + Send + 'a;
34    type ReadDir: FloppyReadDir<'a, Self> + Send + 'a;
35    // type TempDir: FloppyTempDir;
36
37    async fn canonicalize<P: AsRef<Path> + Send>(&self, path: P) -> Result<PathBuf>;
38
39    async fn copy<P: AsRef<Path> + Send>(&self, from: P, to: P) -> Result<u64>;
40
41    async fn create_dir<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
42
43    async fn create_dir_all<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
44
45    async fn hard_link<P: AsRef<Path> + Send>(&self, src: P, dst: P) -> Result<()>;
46
47    async fn metadata<P: AsRef<Path> + Send>(&self, path: P) -> Result<Self::Metadata>;
48
49    async fn read<P: AsRef<Path> + Send>(&self, path: P) -> Result<Vec<u8>>;
50
51    async fn read_dir<P: AsRef<Path> + Send>(&self, path: P) -> Result<Self::ReadDir>;
52
53    async fn read_link<P: AsRef<Path> + Send>(&self, path: P) -> Result<PathBuf>;
54
55    async fn read_to_string<P: AsRef<Path> + Send>(&self, path: P) -> Result<String>;
56
57    async fn remove_dir<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
58
59    async fn remove_dir_all<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
60
61    async fn remove_file<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
62
63    async fn rename<P: AsRef<Path> + Send>(&self, from: P, to: P) -> Result<()>;
64
65    async fn set_permissions<P: AsRef<Path> + Send>(
66        &self,
67        path: P,
68        perm: Self::Permissions,
69    ) -> Result<()>;
70
71    async fn symlink<P: AsRef<Path> + Send>(&self, src: P, dst: P) -> Result<()>;
72
73    async fn symlink_metadata<P: AsRef<Path> + Send>(&self, path: P) -> Result<Self::Metadata>;
74
75    async fn try_exists<P: AsRef<Path> + Send>(&self, path: P) -> Result<bool>;
76
77    async fn write<P: AsRef<Path> + Send>(
78        &self,
79        path: P,
80        contents: impl AsRef<[u8]> + Send,
81    ) -> Result<()>;
82
83    fn new_dir_builder(&'a self) -> Self::DirBuilder;
84
85    /// Search the given directory **non-recursively** for a file or directory
86    /// matching the given needle. If a file is found, return its path.
87    async fn find_in_dir<P: AsRef<Path> + Send, S: Into<String> + Send>(
88        &self,
89        path: P,
90        needle: S,
91    ) -> Result<Option<PathBuf>> {
92        let needle = needle.into();
93        let mut dir = self.read_dir(path).await?;
94        while let Some(entry) = dir.next_entry().await? {
95            let path = entry.path();
96            if let Some(file_name) = path.file_name() {
97                // TODO: This is kinda icky
98                let file_name = file_name.to_string_lossy();
99                if file_name == needle
100                    || file_name.starts_with(&needle)
101                    || file_name.ends_with(&needle)
102                {
103                    return Ok(Some(path));
104                }
105            }
106        }
107        Ok(None)
108    }
109}
110
111#[async_trait::async_trait]
112pub trait FloppyDiskUnixExt {
113    async fn chown<P: Into<PathBuf> + Send>(&self, path: P, uid: u32, gid: u32) -> Result<()>;
114}
115
116#[allow(clippy::len_without_is_empty)]
117#[async_trait::async_trait]
118pub trait FloppyMetadata<'a, Disk: FloppyDisk<'a>>: Debug + std::marker::Unpin + Send {
119    fn file_type(&self) -> Disk::FileType;
120    fn is_dir(&self) -> bool;
121    fn is_file(&self) -> bool;
122    fn is_symlink(&self) -> bool;
123    fn len(&self) -> u64;
124    fn permissions(&self) -> Disk::Permissions;
125    fn modified(&self) -> Result<SystemTime>;
126    fn accessed(&self) -> Result<SystemTime>;
127    fn created(&self) -> Result<SystemTime>;
128}
129
130#[async_trait::async_trait]
131pub trait FloppyUnixMetadata {
132    fn uid(&self) -> Result<u32>;
133    fn gid(&self) -> Result<u32>;
134}
135
136#[async_trait::async_trait]
137pub trait FloppyReadDir<'a, Disk: FloppyDisk<'a>>: Debug + std::marker::Unpin + Send {
138    async fn next_entry(&mut self) -> Result<Option<Disk::DirEntry>>;
139}
140
141pub trait FloppyPermissions: Debug + std::marker::Unpin + Send {
142    fn readonly(&self) -> bool;
143    fn set_readonly(&mut self, readonly: bool);
144}
145
146pub trait FloppyUnixPermissions: Debug + std::marker::Unpin + Send {
147    fn mode(&self) -> u32;
148    fn set_mode(&mut self, mode: u32);
149    fn from_mode(mode: u32) -> Self;
150}
151
152#[async_trait::async_trait]
153pub trait FloppyDirBuilder: Debug + std::marker::Unpin + Send {
154    fn recursive(&mut self, recursive: bool) -> &mut Self;
155    async fn create<P: AsRef<Path> + Send>(&self, path: P) -> Result<()>;
156    #[cfg(unix)]
157    fn mode(&mut self, mode: u32) -> &mut Self;
158}
159
160#[async_trait::async_trait]
161pub trait FloppyDirEntry<'a, Disk: FloppyDisk<'a>>: Debug + std::marker::Unpin + Send {
162    fn path(&self) -> PathBuf;
163    fn file_name(&self) -> OsString;
164    async fn metadata(&self) -> Result<Disk::Metadata>;
165    async fn file_type(&self) -> Result<Disk::FileType>;
166
167    #[cfg(unix)]
168    fn ino(&self) -> u64;
169}
170
171#[async_trait::async_trait]
172pub trait FloppyFile<'a, Disk: FloppyDisk<'a>>:
173    AsyncRead + AsyncWrite + AsyncSeek + Debug + std::marker::Unpin + Send
174{
175    async fn sync_all(&mut self) -> Result<()>;
176    async fn sync_data(&mut self) -> Result<()>;
177    async fn set_len(&mut self, size: u64) -> Result<()>;
178    async fn metadata(&self) -> Result<Disk::Metadata>;
179    async fn try_clone(&'a self) -> Result<Box<Disk::File>>;
180    async fn set_permissions(&self, perm: Disk::Permissions) -> Result<()>;
181    async fn permissions(&self) -> Result<Disk::Permissions>;
182}
183
184#[async_trait::async_trait]
185pub trait FloppyOpenOptions<'a, Disk: FloppyDisk<'a>>: Debug + std::marker::Unpin + Send {
186    fn new() -> Self;
187    fn read(self, read: bool) -> Self;
188    fn write(self, write: bool) -> Self;
189    fn append(self, append: bool) -> Self;
190    fn truncate(self, truncate: bool) -> Self;
191    fn create(self, create: bool) -> Self;
192    fn create_new(self, create_new: bool) -> Self;
193    async fn open<P: AsRef<Path> + Send>(&self, disk: &'a Disk, path: P) -> Result<Disk::File>;
194}
195
196pub trait FloppyFileType: Debug + std::marker::Unpin + Send {
197    fn is_dir(&self) -> bool;
198    fn is_file(&self) -> bool;
199    fn is_symlink(&self) -> bool;
200}
201
202// pub trait FloppyTempDir:
203//     Debug + AsRef<Path> + AsRef<PathBuf> + Send + Sync + Deref<Target = Path>
204// {
205//     fn path(&self) -> &Path;
206// }