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}