mfio_rt/
lib.rs

1//! # mfio-rt
2//!
3//! ## mfio Backed Runtime
4//!
5//! This crate aims to provide building blocks for mfio backed asynchronous runtimes. The traits
6//! have the option to not rely on the standard library. This makes the system great for `no_std`
7//! embedded environments or kernel-side code.
8//!
9//! `native` feature (depends on `std`) enables native implementations of the runtime through
10//! [`NativeRt`] structure.
11//!
12//! `virt` feature enables a virtual in-memory runtime through [`VirtRt`](virt::VirtRt) structure.
13//!
14//! Custom runtimes may be implemented by implementing [`IoBackend`], and any of the runtime
15//! traits, such as [`Fs`] or [`Tcp`].
16//!
17//! ## `no_std`
18//!
19//! Currently, only [`Fs`] is exposed in `no_std` environments. [`Tcp`] depends on structures, such
20//! as [`SocketAddr`](https://doc.rust-lang.org/nightly/core/net/enum.SocketAddr.html) that are
21//! currently not available in `core`. This will change once
22//! [`ip_in_core`](https://github.com/rust-lang/rust/issues/108443) is stabilized.
23
24#![cfg_attr(not(feature = "std"), no_std)]
25#![cfg_attr(docsrs, feature(doc_cfg))]
26
27extern crate alloc;
28
29use alloc::string::String;
30
31use core::future::Future;
32
33use core::time::Duration;
34use futures::Stream;
35use mfio::backend::*;
36use mfio::error::Result as MfioResult;
37use mfio::io::NoPos;
38use mfio::stdeq::{AsyncRead, AsyncWrite};
39use serde::{Deserialize, Serialize};
40
41#[cfg(feature = "std")]
42use std::net::{SocketAddr, ToSocketAddrs};
43
44#[cfg(feature = "std")]
45pub use std::path::{Component, Path, PathBuf};
46
47// We may later consider supporting non-unix paths in no_std scenarios, but currently, this is not
48// the case, because TypedPath requires a lifetime argument.
49#[cfg(not(feature = "std"))]
50pub use typed_path::{UnixComponent as Component, UnixPath as Path, UnixPathBuf as PathBuf};
51
52#[cfg(feature = "native")]
53#[cfg_attr(docsrs, doc(cfg(feature = "native")))]
54pub mod native;
55mod util;
56#[cfg(any(feature = "virt", test, miri))]
57#[cfg_attr(docsrs, doc(cfg(feature = "virt")))]
58pub mod virt;
59
60#[doc(hidden)]
61pub mod __doctest;
62
63#[cfg(any(feature = "test_suite", test))]
64#[cfg_attr(docsrs, doc(cfg(feature = "test_suite")))]
65pub mod test_suite;
66
67#[cfg(feature = "native")]
68pub use native::{NativeFile, NativeRt, NativeRtBuilder};
69
70/// File open options.
71///
72/// This type is equivalent to [`OpenOptions`](std::fs::OpenOptions) found in the standard library,
73/// but omits `append` mode.
74#[repr(C)]
75#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
76pub struct OpenOptions {
77    pub read: bool,
78    pub write: bool,
79    pub create: bool,
80    pub create_new: bool,
81    pub truncate: bool,
82    // Append would currently require us to get file pos after opening.
83    // So we don't support it at the moment.
84    //pub append: bool,
85}
86
87impl OpenOptions {
88    pub const fn new() -> Self {
89        Self {
90            read: false,
91            write: false,
92            create: false,
93            create_new: false,
94            truncate: false,
95        }
96    }
97
98    pub fn read(self, read: bool) -> Self {
99        Self { read, ..self }
100    }
101
102    pub fn write(self, write: bool) -> Self {
103        Self { write, ..self }
104    }
105
106    pub fn create(self, create: bool) -> Self {
107        Self { create, ..self }
108    }
109
110    pub fn create_new(self, create_new: bool) -> Self {
111        Self { create_new, ..self }
112    }
113
114    pub fn truncate(self, truncate: bool) -> Self {
115        Self { truncate, ..self }
116    }
117}
118
119/// Network stream shutdown options.
120///
121/// This type is equivalent to [`Shutdown`](std::net::Shutdown) in the standard library.
122#[repr(C)]
123#[derive(Clone, Copy, Debug, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord)]
124pub enum Shutdown {
125    Read,
126    Write,
127    Both,
128}
129
130#[cfg(feature = "std")]
131use std::net;
132
133#[cfg(feature = "std")]
134impl From<net::Shutdown> for Shutdown {
135    fn from(o: net::Shutdown) -> Self {
136        match o {
137            net::Shutdown::Write => Self::Write,
138            net::Shutdown::Read => Self::Read,
139            net::Shutdown::Both => Self::Both,
140        }
141    }
142}
143
144#[cfg(feature = "std")]
145impl From<Shutdown> for net::Shutdown {
146    fn from(o: Shutdown) -> Self {
147        match o {
148            Shutdown::Write => Self::Write,
149            Shutdown::Read => Self::Read,
150            Shutdown::Both => Self::Both,
151        }
152    }
153}
154
155/// Primary filesystem trait.
156///
157/// This provides an entrypoint for filesystem operations. However, since operations are typically
158/// performed on a directory, this trait only serves as a proxy for retrieving the current
159/// directory handle.
160pub trait Fs: IoBackend {
161    type DirHandle<'a>: DirHandle + 'a
162    where
163        Self: 'a;
164
165    /// Gets a directory handle representing current working directory.
166    ///
167    /// Note that implementor is not required to maintain the location of this handle constant as
168    /// CWD changes. Therefore, the handle may point to different locations as the directory gets
169    /// changed in this program.
170    fn current_dir(&self) -> &Self::DirHandle<'_>;
171
172    fn open<'a>(
173        &'a self,
174        path: &'a Path,
175        options: OpenOptions,
176    ) -> <Self::DirHandle<'a> as DirHandle>::OpenFileFuture<'a> {
177        self.current_dir().open_file(path, options)
178    }
179}
180
181/// Represents a location in filesystem operations are performed from.
182///
183/// Directory handles may refer to fixed directory entries throughout time, even if said entry is
184/// unlinked from the filesystem. So long as the handle is held, it may be valid. However, this
185/// behavior is implementation-specific, and, for instance, [`NativeRt`] does not follow it,
186/// because directory handles are simply stored as paths, rather than dir FDs/handles.
187pub trait DirHandle: Sized {
188    type FileHandle: FileHandle;
189    type OpenFileFuture<'a>: Future<Output = MfioResult<Self::FileHandle>> + 'a
190    where
191        Self: 'a;
192    type PathFuture<'a>: Future<Output = MfioResult<PathBuf>> + 'a
193    where
194        Self: 'a;
195    type OpenDirFuture<'a>: Future<Output = MfioResult<Self>> + 'a
196    where
197        Self: 'a;
198    type ReadDir<'a>: Stream<Item = MfioResult<DirEntry>> + 'a
199    where
200        Self: 'a;
201    type ReadDirFuture<'a>: Future<Output = MfioResult<Self::ReadDir<'a>>> + 'a
202    where
203        Self: 'a;
204    type MetadataFuture<'a>: Future<Output = MfioResult<Metadata>> + 'a
205    where
206        Self: 'a;
207    type OpFuture<'a>: Future<Output = MfioResult<()>> + 'a
208    where
209        Self: 'a;
210
211    /// Gets the absolute path to this `DirHandle`.
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
217    /// # mfio_rt::__doctest::run_each(|fs| async {
218    /// use mfio_rt::{DirHandle, Fs};
219    /// // On no_std mfio_rt re-exports typed_path::UnixPath as Path
220    /// use mfio_rt::Path;
221    ///
222    /// let dir = fs.current_dir();
223    ///
224    /// let path = dir.path().await.unwrap();
225    ///
226    /// assert_ne!(path, Path::new("/dev"));
227    /// # });
228    /// ```
229    fn path(&self) -> Self::PathFuture<'_>;
230
231    /// Reads the directory contents.
232    ///
233    /// This function returns a stream that can be used to list files and subdirectories within
234    /// this dir. Any errors will be propagated through the stream.
235    ///
236    /// # Examples
237    ///
238    /// ```
239    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
240    /// # mfio_rt::__doctest::run_each(|fs| async {
241    /// use futures::StreamExt;
242    /// use mfio::error::Error;
243    /// use mfio_rt::{DirHandle, Fs};
244    ///
245    /// let dir = fs.current_dir();
246    ///
247    /// let mut entries = dir
248    ///     .read_dir()
249    ///     .await
250    ///     .unwrap()
251    ///     .filter_map(|res| async { res.ok() })
252    ///     .map(|res| res.name)
253    ///     .collect::<Vec<_>>()
254    ///     .await;
255    ///
256    /// assert!(entries.contains(&"Cargo.toml".to_string()));
257    ///
258    /// // The order in which `read_dir` returns entries is not guaranteed. If reproducible
259    /// // ordering is required the entries should be explicitly sorted.
260    ///
261    /// entries.sort();
262    ///
263    /// // The entries have now been sorted by their path.
264    ///
265    /// # });
266    /// ```
267    fn read_dir(&self) -> Self::ReadDirFuture<'_>;
268
269    /// Opens a file.
270    ///
271    /// This function accepts an absolute or relative path to a file for reading. If the path is
272    /// relative, it is opened relative to this `DirHandle`.
273    ///
274    /// # Examples
275    ///
276    /// ```
277    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
278    /// # mfio_rt::__doctest::run_each(|fs| async {
279    /// use mfio::traits::IoRead;
280    /// use mfio_rt::{DirHandle, Fs, OpenOptions};
281    ///
282    /// let dir = fs.current_dir();
283    ///
284    /// let fh = dir
285    ///     .open_file("Cargo.toml", OpenOptions::new().read(true))
286    ///     .await
287    ///     .unwrap();
288    ///
289    /// // Now you may do file operations, like reading it
290    ///
291    /// let mut bytes = vec![];
292    /// fh.read_to_end(0, &mut bytes).await.unwrap();
293    /// let s = String::from_utf8(bytes).unwrap();
294    ///
295    /// assert!(s.contains("mfio"));
296    /// # });
297    /// ```
298    fn open_file<'a, P: AsRef<Path> + ?Sized>(
299        &'a self,
300        path: &'a P,
301        options: OpenOptions,
302    ) -> Self::OpenFileFuture<'a>;
303
304    /// Opens a directory.
305    ///
306    /// This function accepts an absolute or relative path to a directory for reading. If the path
307    /// is relative, it is opened relative to this `DirHandle`.
308    ///
309    /// # Examples
310    ///
311    /// ```
312    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
313    /// # mfio_rt::__doctest::run_each(|fs| async {
314    /// use futures::StreamExt;
315    /// use mfio::traits::IoRead;
316    /// use mfio_rt::{DirHandle, Fs, OpenOptions};
317    ///
318    /// let dir = fs.current_dir();
319    ///
320    /// let subdir = dir.open_dir("src").await.unwrap();
321    ///
322    /// assert_ne!(dir.path().await.unwrap(), subdir.path().await.unwrap());
323    ///
324    /// // Now you may do directory operations, like listing it
325    ///
326    /// let mut entries = subdir
327    ///     .read_dir()
328    ///     .await
329    ///     .unwrap()
330    ///     .filter_map(|res| async { res.ok() })
331    ///     .map(|res| res.name)
332    ///     .collect::<Vec<_>>()
333    ///     .await;
334    ///
335    /// assert!(entries.contains(&"lib.rs".to_string()));
336    ///
337    /// # });
338    /// ```
339    fn open_dir<'a, P: AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> Self::OpenDirFuture<'a>;
340
341    /// Retrieves file metadata.
342    ///
343    /// # Examples
344    ///
345    /// ```
346    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
347    /// # mfio_rt::__doctest::run_each(|fs| async {
348    /// # });
349    /// ```
350    fn metadata<'a, P: AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> Self::MetadataFuture<'a>;
351
352    /// Do an operation.
353    ///
354    /// This function performs an operation from the [`DirOp`] enum.
355    ///
356    /// # Examples
357    ///
358    /// ```
359    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
360    /// # mfio_rt::__doctest::run_each(|fs| async {
361    /// # });
362    /// ```
363    fn do_op<'a, P: AsRef<Path> + ?Sized>(&'a self, operation: DirOp<&'a P>) -> Self::OpFuture<'a>;
364}
365
366/// Helpers for running directory operations more ergonomically.
367pub trait DirHandleExt: DirHandle {
368    ///
369    /// # Examples
370    ///
371    /// ```
372    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
373    /// # mfio_rt::__doctest::run_each(|fs| async {
374    /// # });
375    /// ```
376    fn set_permissions<'a, P: AsRef<Path> + ?Sized>(
377        &'a self,
378        path: &'a P,
379        permissions: Permissions,
380    ) -> Self::OpFuture<'a> {
381        self.do_op(DirOp::SetPermissions { path, permissions })
382    }
383
384    ///
385    /// # Examples
386    ///
387    /// ```
388    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
389    /// # mfio_rt::__doctest::run_each(|fs| async {
390    /// # });
391    /// ```
392    fn remove_dir<'a, P: AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> Self::OpFuture<'a> {
393        self.do_op(DirOp::RemoveDir { path })
394    }
395
396    ///
397    /// # Examples
398    ///
399    /// ```
400    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
401    /// # mfio_rt::__doctest::run_each(|fs| async {
402    /// # });
403    /// ```
404    fn remove_dir_all<'a, P: AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> Self::OpFuture<'a> {
405        self.do_op(DirOp::RemoveDirAll { path })
406    }
407
408    ///
409    /// # Examples
410    ///
411    /// ```
412    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
413    /// # mfio_rt::__doctest::run_each(|fs| async {
414    /// # });
415    /// ```
416    fn create_dir<'a, P: AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> Self::OpFuture<'a> {
417        self.do_op(DirOp::CreateDir { path })
418    }
419
420    ///
421    /// # Examples
422    ///
423    /// ```
424    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
425    /// # mfio_rt::__doctest::run_each(|fs| async {
426    /// # });
427    /// ```
428    fn create_dir_all<'a, P: AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> Self::OpFuture<'a> {
429        self.do_op(DirOp::CreateDirAll { path })
430    }
431
432    ///
433    /// # Examples
434    ///
435    /// ```
436    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
437    /// # mfio_rt::__doctest::run_each(|fs| async {
438    /// # });
439    /// ```
440    fn remove_file<'a, P: AsRef<Path> + ?Sized>(&'a self, path: &'a P) -> Self::OpFuture<'a> {
441        self.do_op(DirOp::RemoveFile { path })
442    }
443
444    ///
445    /// # Examples
446    ///
447    /// ```
448    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
449    /// # mfio_rt::__doctest::run_each(|fs| async {
450    /// # });
451    /// ```
452    fn rename<'a, P: AsRef<Path> + ?Sized>(&'a self, from: &'a P, to: &'a P) -> Self::OpFuture<'a> {
453        self.do_op(DirOp::Rename { from, to })
454    }
455
456    ///
457    /// # Examples
458    ///
459    /// ```
460    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
461    /// # mfio_rt::__doctest::run_each(|fs| async {
462    /// # });
463    /// ```
464    // TODO: reflinking option
465    fn copy<'a, P: AsRef<Path> + ?Sized>(&'a self, from: &'a P, to: &'a P) -> Self::OpFuture<'a> {
466        self.do_op(DirOp::Copy { from, to })
467    }
468
469    ///
470    /// # Examples
471    ///
472    /// ```
473    /// # #[cfg(any(miri, feature = "std", feature = "virt"))]
474    /// # mfio_rt::__doctest::run_each(|fs| async {
475    /// # });
476    /// ```
477    fn hard_link<'a, P: AsRef<Path> + ?Sized>(
478        &'a self,
479        from: &'a P,
480        to: &'a P,
481    ) -> Self::OpFuture<'a> {
482        self.do_op(DirOp::HardLink { from, to })
483    }
484
485    // TODO: decide on how to handle symlinks, as they differ between platforms.
486    // fn symlink_file(&self, from: &Path, to: &Path) -> Self::OpFuture<'_>;
487    // fn symlink_dir(&self, from: &Path, to: &Path) -> Self::OpFuture<'_>;
488}
489
490impl<T: DirHandle> DirHandleExt for T {}
491
492/// List of operations that can be done on a filesystem.
493#[non_exhaustive]
494#[derive(Serialize, Deserialize, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug)]
495pub enum DirOp<P: AsRef<Path>> {
496    SetPermissions { path: P, permissions: Permissions },
497    RemoveDir { path: P },
498    RemoveDirAll { path: P },
499    CreateDir { path: P },
500    CreateDirAll { path: P },
501    RemoveFile { path: P },
502    Rename { from: P, to: P },
503    Copy { from: P, to: P },
504    HardLink { from: P, to: P },
505}
506
507impl<P: AsRef<Path>> DirOp<P> {
508    pub fn as_path(&self) -> DirOp<&Path> {
509        match self {
510            Self::SetPermissions { path, permissions } => DirOp::SetPermissions {
511                path: path.as_ref(),
512                permissions: *permissions,
513            },
514            Self::RemoveDir { path } => DirOp::RemoveDir {
515                path: path.as_ref(),
516            },
517            Self::RemoveDirAll { path } => DirOp::RemoveDirAll {
518                path: path.as_ref(),
519            },
520            Self::CreateDir { path } => DirOp::CreateDir {
521                path: path.as_ref(),
522            },
523            Self::CreateDirAll { path } => DirOp::CreateDirAll {
524                path: path.as_ref(),
525            },
526            Self::RemoveFile { path } => DirOp::RemoveFile {
527                path: path.as_ref(),
528            },
529            Self::Rename { from, to } => DirOp::Rename {
530                from: from.as_ref(),
531                to: to.as_ref(),
532            },
533            Self::Copy { from, to } => DirOp::Copy {
534                from: from.as_ref(),
535                to: to.as_ref(),
536            },
537            Self::HardLink { from, to } => DirOp::HardLink {
538                from: from.as_ref(),
539                to: to.as_ref(),
540            },
541        }
542    }
543}
544
545impl<'a, P: AsRef<Path> + ?Sized> DirOp<&'a P> {
546    pub fn into_path(self) -> DirOp<&'a Path> {
547        match self {
548            Self::SetPermissions { path, permissions } => DirOp::SetPermissions {
549                path: path.as_ref(),
550                permissions,
551            },
552            Self::RemoveDir { path } => DirOp::RemoveDir {
553                path: path.as_ref(),
554            },
555            Self::RemoveDirAll { path } => DirOp::RemoveDirAll {
556                path: path.as_ref(),
557            },
558            Self::CreateDir { path } => DirOp::CreateDir {
559                path: path.as_ref(),
560            },
561            Self::CreateDirAll { path } => DirOp::CreateDirAll {
562                path: path.as_ref(),
563            },
564            Self::RemoveFile { path } => DirOp::RemoveFile {
565                path: path.as_ref(),
566            },
567            Self::Rename { from, to } => DirOp::Rename {
568                from: from.as_ref(),
569                to: to.as_ref(),
570            },
571            Self::Copy { from, to } => DirOp::Copy {
572                from: from.as_ref(),
573                to: to.as_ref(),
574            },
575            Self::HardLink { from, to } => DirOp::HardLink {
576                from: from.as_ref(),
577                to: to.as_ref(),
578            },
579        }
580    }
581
582    pub fn into_pathbuf(self) -> DirOp<PathBuf> {
583        match self {
584            Self::SetPermissions { path, permissions } => DirOp::SetPermissions {
585                path: path.as_ref().into(),
586                permissions,
587            },
588            Self::RemoveDir { path } => DirOp::RemoveDir {
589                path: path.as_ref().into(),
590            },
591            Self::RemoveDirAll { path } => DirOp::RemoveDirAll {
592                path: path.as_ref().into(),
593            },
594            Self::CreateDir { path } => DirOp::CreateDir {
595                path: path.as_ref().into(),
596            },
597            Self::CreateDirAll { path } => DirOp::CreateDirAll {
598                path: path.as_ref().into(),
599            },
600            Self::RemoveFile { path } => DirOp::RemoveFile {
601                path: path.as_ref().into(),
602            },
603            Self::Rename { from, to } => DirOp::Rename {
604                from: from.as_ref().into(),
605                to: to.as_ref().into(),
606            },
607            Self::Copy { from, to } => DirOp::Copy {
608                from: from.as_ref().into(),
609                to: to.as_ref().into(),
610            },
611            Self::HardLink { from, to } => DirOp::HardLink {
612                from: from.as_ref().into(),
613                to: to.as_ref().into(),
614            },
615        }
616    }
617
618    pub fn into_string(self) -> DirOp<String> {
619        match self {
620            Self::SetPermissions { path, permissions } => DirOp::SetPermissions {
621                path: path.as_ref().to_string_lossy().into(),
622                permissions,
623            },
624            Self::RemoveDir { path } => DirOp::RemoveDir {
625                path: path.as_ref().to_string_lossy().into(),
626            },
627            Self::RemoveDirAll { path } => DirOp::RemoveDirAll {
628                path: path.as_ref().to_string_lossy().into(),
629            },
630            Self::CreateDir { path } => DirOp::CreateDir {
631                path: path.as_ref().to_string_lossy().into(),
632            },
633            Self::CreateDirAll { path } => DirOp::CreateDirAll {
634                path: path.as_ref().to_string_lossy().into(),
635            },
636            Self::RemoveFile { path } => DirOp::RemoveFile {
637                path: path.as_ref().to_string_lossy().into(),
638            },
639            Self::Rename { from, to } => DirOp::Rename {
640                from: from.as_ref().to_string_lossy().into(),
641                to: to.as_ref().to_string_lossy().into(),
642            },
643            Self::Copy { from, to } => DirOp::Copy {
644                from: from.as_ref().to_string_lossy().into(),
645                to: to.as_ref().to_string_lossy().into(),
646            },
647            Self::HardLink { from, to } => DirOp::HardLink {
648                from: from.as_ref().to_string_lossy().into(),
649                to: to.as_ref().to_string_lossy().into(),
650            },
651        }
652    }
653}
654
655/// Directory list entry.
656///
657/// This type is equivalent to [`DirEntry`](std::fs::DirEntry) in the standard library.
658#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, Ord, PartialOrd)]
659pub struct DirEntry {
660    pub name: String,
661    pub ty: FileType,
662}
663
664#[cfg(feature = "std")]
665impl From<std::fs::DirEntry> for DirEntry {
666    fn from(d: std::fs::DirEntry) -> Self {
667        let ty = d
668            .file_type()
669            .map(|ty| {
670                if ty.is_file() {
671                    FileType::File
672                } else if ty.is_dir() {
673                    FileType::Directory
674                } else if ty.is_symlink() {
675                    FileType::Symlink
676                } else {
677                    FileType::Unknown
678                }
679            })
680            .unwrap_or(FileType::Unknown);
681
682        Self {
683            name: d.file_name().to_string_lossy().into(),
684            ty,
685        }
686    }
687}
688
689/// Directory list entry type.
690///
691/// This type is equivalent to [`FileType`](std::fs::FileType) in the standard library.
692#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
693pub enum FileType {
694    Unknown,
695    File,
696    Directory,
697    Symlink,
698}
699
700/// Directory list entry permission.
701///
702/// This type is equivalent to [`Permission`](std::fs::Permissions) in the standard library.
703/// However, this currently contains nothing, and is effectively useless.
704///
705/// TODO: make this type do something.
706#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Debug, Serialize, Deserialize)]
707pub struct Permissions {}
708
709#[cfg(feature = "std")]
710impl From<std::fs::Permissions> for Permissions {
711    fn from(_: std::fs::Permissions) -> Self {
712        Self {}
713    }
714}
715
716/// Directory list entry metadata.
717///
718/// This type is equivalent to [`Metadata`](std::fs::Metadata) in the standard library.
719#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
720pub struct Metadata {
721    pub permissions: Permissions,
722    pub len: u64,
723    /// Modified time (since unix epoch, or other point)
724    pub modified: Option<Duration>,
725    /// Accessed time (since unix epoch, or other point)
726    pub accessed: Option<Duration>,
727    /// Created time (since unix epoch, or other point)
728    pub created: Option<Duration>,
729}
730
731impl Metadata {
732    pub fn empty_file(permissions: Permissions, created: Option<Duration>) -> Self {
733        Self {
734            permissions,
735            len: 0,
736            modified: None,
737            accessed: None,
738            created,
739        }
740    }
741
742    pub fn empty_dir(permissions: Permissions, created: Option<Duration>) -> Self {
743        Self::empty_file(permissions, created)
744    }
745}
746
747/// Supertrait for file handles.
748pub trait FileHandle: AsyncRead<u64> + AsyncWrite<u64> {}
749impl<T: AsyncRead<u64> + AsyncWrite<u64>> FileHandle for T {}
750
751/// Supertrait for stream handles.
752pub trait StreamHandle: AsyncRead<NoPos> + AsyncWrite<NoPos> {}
753impl<T: AsyncRead<NoPos> + AsyncWrite<NoPos>> StreamHandle for T {}
754
755/// Describes TCP capable runtime operations.
756#[cfg(feature = "std")]
757#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
758pub trait Tcp: IoBackend {
759    type StreamHandle: TcpStreamHandle;
760    type ListenerHandle: TcpListenerHandle<StreamHandle = Self::StreamHandle>;
761    type ConnectFuture<'a, A: ToSocketAddrs + Send + 'a>: Future<Output = MfioResult<Self::StreamHandle>>
762        + 'a
763    where
764        Self: 'a;
765    type BindFuture<'a, A: ToSocketAddrs + Send + 'a>: Future<Output = MfioResult<Self::ListenerHandle>>
766        + 'a
767    where
768        Self: 'a;
769
770    fn connect<'a, A: ToSocketAddrs + Send + 'a>(&'a self, addrs: A) -> Self::ConnectFuture<'a, A>;
771
772    fn bind<'a, A: ToSocketAddrs + Send + 'a>(&'a self, addrs: A) -> Self::BindFuture<'a, A>;
773}
774
775/// Describes operations performable on a TCP connection.
776#[cfg(feature = "std")]
777#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
778pub trait TcpStreamHandle: StreamHandle {
779    fn local_addr(&self) -> MfioResult<SocketAddr>;
780    fn peer_addr(&self) -> MfioResult<SocketAddr>;
781    fn shutdown(&self, how: Shutdown) -> MfioResult<()>;
782
783    // These interfaces may be slightly trickier to implement, so we omit them for now.
784    //fn set_ttl(&self, ttl: u32) -> MfioResult<()>;
785    //fn ttl(&self) -> MfioResult<u32>;
786    //fn set_nodelay(&self, nodelay: bool) -> MfioResult<()>;
787    //fn nodelay(&self) -> MfioResult<bool>;
788}
789
790/// Describes operations performable on a TCP listener.
791#[cfg(feature = "std")]
792#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
793pub trait TcpListenerHandle: Stream<Item = (Self::StreamHandle, SocketAddr)> {
794    type StreamHandle: TcpStreamHandle;
795
796    fn local_addr(&self) -> MfioResult<SocketAddr>;
797
798    // These interfaces may be slightly trickier to implement, so we omit them for now.
799    //fn set_ttl(&self, ttl: u32) -> MfioResult<()>;
800    //fn ttl(&self) -> MfioResult<u32>;
801}