Skip to main content

sys_traits/
lib.rs

1use core::str;
2use std::borrow::Cow;
3use std::env::VarError;
4use std::ffi::OsStr;
5use std::ffi::OsString;
6use std::io;
7use std::io::Error;
8use std::io::ErrorKind;
9use std::path::Path;
10use std::path::PathBuf;
11use std::time::SystemTime;
12
13pub mod boxed;
14pub mod ctx;
15pub mod impls;
16
17pub use sys_traits_macros::auto_impl;
18
19pub use self::ctx::FsFileWithPathsInErrors;
20pub use self::ctx::OperationError;
21pub use self::ctx::OperationErrorKind;
22pub use self::ctx::PathsInErrorsExt;
23pub use self::ctx::SysWithPathsInErrors;
24
25use self::boxed::BoxedFsFile;
26use self::boxed::BoxedFsMetadataValue;
27
28// #### ENVIRONMENT ####
29
30// == EnvCurrentDir ==
31
32pub trait EnvCurrentDir {
33  fn env_current_dir(&self) -> io::Result<PathBuf>;
34}
35
36// == EnvSetCurrentDir ==
37
38pub trait BaseEnvSetCurrentDir {
39  #[doc(hidden)]
40  fn base_env_set_current_dir(&self, path: &Path) -> io::Result<()>;
41}
42
43pub trait EnvSetCurrentDir: BaseEnvSetCurrentDir {
44  #[inline]
45  fn env_set_current_dir(&self, path: impl AsRef<Path>) -> io::Result<()> {
46    self.base_env_set_current_dir(path.as_ref())
47  }
48}
49
50impl<T: BaseEnvSetCurrentDir> EnvSetCurrentDir for T {}
51
52// == EnvVar ==
53
54pub trait BaseEnvVar {
55  #[doc(hidden)]
56  fn base_env_var_os(&self, key: &OsStr) -> Option<OsString>;
57}
58
59pub trait EnvVar: BaseEnvVar {
60  #[inline]
61  fn env_var_os(&self, key: impl AsRef<OsStr>) -> Option<OsString> {
62    self.base_env_var_os(key.as_ref())
63  }
64
65  fn env_var(&self, key: impl AsRef<OsStr>) -> Result<String, VarError> {
66    match self.env_var_os(key) {
67      Some(val) => val.into_string().map_err(VarError::NotUnicode),
68      None => Err(VarError::NotPresent),
69    }
70  }
71
72  /// Helper to get a path from an environment variable.
73  fn env_var_path(&self, key: impl AsRef<OsStr>) -> Option<PathBuf> {
74    self
75      .env_var_os(key)
76      .and_then(|h| if h.is_empty() { None } else { Some(h) })
77      .map(|value| {
78        #[cfg(all(target_arch = "wasm32", feature = "wasm"))]
79        {
80          impls::wasm_string_to_path(value.to_string_lossy().to_string())
81        }
82        #[cfg(any(not(target_arch = "wasm32"), not(feature = "wasm")))]
83        {
84          PathBuf::from(value)
85        }
86      })
87  }
88}
89
90impl<T: BaseEnvVar> EnvVar for T {}
91
92// == EnvRemoveVar ==
93
94pub trait BaseEnvRemoveVar {
95  #[doc(hidden)]
96  fn base_env_remove_var(&self, key: &OsStr);
97}
98
99pub trait EnvRemoveVar: BaseEnvRemoveVar {
100  fn env_remove_var(&self, key: impl AsRef<OsStr>) {
101    self.base_env_remove_var(key.as_ref())
102  }
103}
104
105impl<T: BaseEnvRemoveVar> EnvRemoveVar for T {}
106
107// == EnvSetVar ==
108
109pub trait BaseEnvSetVar {
110  #[doc(hidden)]
111  fn base_env_set_var(&self, key: &OsStr, value: &OsStr);
112}
113
114pub trait EnvSetVar: BaseEnvSetVar {
115  fn env_set_var(&self, key: impl AsRef<OsStr>, value: impl AsRef<OsStr>) {
116    self.base_env_set_var(key.as_ref(), value.as_ref())
117  }
118}
119
120impl<T: BaseEnvSetVar> EnvSetVar for T {}
121
122// == EnvUmask ==
123
124pub trait EnvUmask {
125  fn env_umask(&self) -> io::Result<u32>;
126}
127
128// == EnvSetUmask ==
129
130pub trait EnvSetUmask {
131  fn env_set_umask(&self, umask: u32) -> io::Result<u32>;
132}
133
134// == EnvCacheDir ==
135
136pub trait EnvCacheDir {
137  fn env_cache_dir(&self) -> Option<PathBuf>;
138}
139
140// == EnvHomeDir ==
141
142pub trait EnvHomeDir {
143  fn env_home_dir(&self) -> Option<PathBuf>;
144}
145
146// == EnvProgramsDir ==
147
148pub trait EnvProgramsDir {
149  fn env_programs_dir(&self) -> Option<PathBuf>;
150}
151
152// == EnvTempDir ==
153
154pub trait EnvTempDir {
155  fn env_temp_dir(&self) -> io::Result<PathBuf>;
156}
157
158// #### FILE SYSTEM ####
159
160#[cfg(windows)]
161type CustomFlagsValue = u32;
162#[cfg(not(windows))]
163type CustomFlagsValue = i32;
164
165#[derive(Default, Debug, Clone)]
166#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
167#[cfg_attr(feature = "serde", serde(default, rename_all = "camelCase"))]
168#[non_exhaustive] // so we can add properties without breaking people
169pub struct OpenOptions {
170  pub read: bool,
171  pub write: bool,
172  pub create: bool,
173  pub truncate: bool,
174  pub append: bool,
175  pub create_new: bool,
176  /// Unix only. Ignored on Windows.
177  pub mode: Option<u32>,
178  /// Custom flags to set on Unix or Windows.
179  ///
180  /// On Windows this is a u32, but on Unix it's an i32.
181  ///
182  /// Note: only provide flags that make sense for the current operating system.
183  pub custom_flags: Option<CustomFlagsValue>,
184  /// Windows only. Ignored on Unix.
185  pub access_mode: Option<u32>,
186  /// Windows only. Ignored on Unix.
187  pub share_mode: Option<u32>,
188  /// Windows only. Ignored on Unix.
189  pub attributes: Option<u32>,
190  /// Windows only. Ignored on Unix.
191  pub security_qos_flags: Option<u32>,
192}
193
194impl OpenOptions {
195  pub fn new() -> Self {
196    Self::default()
197  }
198
199  pub fn new_read() -> Self {
200    Self {
201      read: true,
202      write: false,
203      create: false,
204      truncate: false,
205      append: false,
206      create_new: false,
207      ..Default::default()
208    }
209  }
210
211  // todo: make this an instance method in the next version
212  #[deprecated(note = "use `new_write` instead")]
213  pub fn write() -> Self {
214    Self::new_write()
215  }
216
217  pub fn new_write() -> Self {
218    Self {
219      read: false,
220      write: true,
221      create: true,
222      truncate: true,
223      append: false,
224      create_new: false,
225      ..Default::default()
226    }
227  }
228
229  pub fn new_append() -> Self {
230    Self {
231      read: false,
232      write: true,
233      create: false,
234      truncate: false,
235      append: true,
236      create_new: false,
237      ..Default::default()
238    }
239  }
240
241  #[inline]
242  pub fn read(&mut self) -> &mut Self {
243    self.read = true;
244    self
245  }
246
247  #[inline]
248  pub fn create(&mut self) -> &mut Self {
249    self.create = true;
250    self
251  }
252
253  #[inline]
254  pub fn truncate(&mut self) -> &mut Self {
255    self.truncate = true;
256    self
257  }
258
259  #[inline]
260  pub fn append(&mut self) -> &mut Self {
261    self.append = true;
262    self
263  }
264
265  #[inline]
266  pub fn create_new(&mut self) -> &mut Self {
267    self.create_new = true;
268    self
269  }
270
271  #[inline]
272  pub fn mode(&mut self, mode: u32) -> &mut Self {
273    self.mode = Some(mode);
274    self
275  }
276
277  #[inline]
278  pub fn custom_flags(&mut self, flags: CustomFlagsValue) -> &mut Self {
279    self.custom_flags = Some(flags);
280    self
281  }
282
283  #[inline]
284  pub fn access_mode(&mut self, value: u32) -> &mut Self {
285    self.access_mode = Some(value);
286    self
287  }
288
289  #[inline]
290  pub fn share_mode(&mut self, value: u32) -> &mut Self {
291    self.share_mode = Some(value);
292    self
293  }
294
295  #[inline]
296  pub fn attributes(&mut self, value: u32) -> &mut Self {
297    self.attributes = Some(value);
298    self
299  }
300
301  #[inline]
302  pub fn security_qos_flags(&mut self, value: u32) -> &mut Self {
303    self.security_qos_flags = Some(value);
304    self
305  }
306}
307
308// == FsCanonicalize ==
309
310pub trait BaseFsCanonicalize {
311  #[doc(hidden)]
312  fn base_fs_canonicalize(&self, path: &Path) -> io::Result<PathBuf>;
313}
314
315pub trait FsCanonicalize: BaseFsCanonicalize {
316  #[inline]
317  fn fs_canonicalize(&self, path: impl AsRef<Path>) -> io::Result<PathBuf> {
318    self.base_fs_canonicalize(path.as_ref())
319  }
320}
321
322impl<T: BaseFsCanonicalize> FsCanonicalize for T {}
323
324// == FsChown ==
325
326pub trait BaseFsChown {
327  #[doc(hidden)]
328  fn base_fs_chown(
329    &self,
330    path: &Path,
331    uid: Option<u32>,
332    gid: Option<u32>,
333  ) -> io::Result<()>;
334}
335
336pub trait FsChown: BaseFsChown {
337  #[inline]
338  fn fs_chown(
339    &self,
340    path: impl AsRef<Path>,
341    uid: Option<u32>,
342    gid: Option<u32>,
343  ) -> io::Result<()> {
344    self.base_fs_chown(path.as_ref(), uid, gid)
345  }
346}
347
348impl<T: BaseFsChown> FsChown for T {}
349
350// == FsSymlinkChown ==
351
352pub trait BaseFsSymlinkChown {
353  #[doc(hidden)]
354  fn base_fs_symlink_chown(
355    &self,
356    path: &Path,
357    uid: Option<u32>,
358    gid: Option<u32>,
359  ) -> io::Result<()>;
360}
361
362pub trait FsSymlinkChown: BaseFsSymlinkChown {
363  #[inline]
364  fn fs_symlink_chown(
365    &self,
366    path: impl AsRef<Path>,
367    uid: Option<u32>,
368    gid: Option<u32>,
369  ) -> io::Result<()> {
370    self.base_fs_symlink_chown(path.as_ref(), uid, gid)
371  }
372}
373
374impl<T: BaseFsSymlinkChown> FsSymlinkChown for T {}
375
376// == FsCloneFile ==
377
378pub trait BaseFsCloneFile {
379  #[doc(hidden)]
380  fn base_fs_clone_file(&self, from: &Path, to: &Path) -> io::Result<()>;
381}
382
383pub trait FsCloneFile: BaseFsCloneFile {
384  #[inline]
385  fn fs_clone_file(
386    &self,
387    from: impl AsRef<Path>,
388    to: impl AsRef<Path>,
389  ) -> io::Result<()> {
390    self.base_fs_clone_file(from.as_ref(), to.as_ref())
391  }
392}
393
394impl<T: BaseFsCloneFile> FsCloneFile for T {}
395
396// == FsCopy ==
397
398pub trait BaseFsCopy {
399  #[doc(hidden)]
400  fn base_fs_copy(&self, from: &Path, to: &Path) -> io::Result<u64>;
401}
402
403pub trait FsCopy: BaseFsCopy {
404  #[inline]
405  fn fs_copy(
406    &self,
407    from: impl AsRef<Path>,
408    to: impl AsRef<Path>,
409  ) -> io::Result<u64> {
410    self.base_fs_copy(from.as_ref(), to.as_ref())
411  }
412}
413
414impl<T: BaseFsCopy> FsCopy for T {}
415
416// == FsCreateDir ==
417
418#[derive(Default, Debug, Clone, Copy)]
419#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
420#[cfg_attr(feature = "serde", serde(default, rename_all = "camelCase"))]
421#[non_exhaustive] // so we can add properties without breaking people
422pub struct CreateDirOptions {
423  pub recursive: bool,
424  /// Unix only. Ignored on Windows.
425  pub mode: Option<u32>,
426}
427
428impl CreateDirOptions {
429  pub fn new() -> Self {
430    Self::default()
431  }
432
433  pub fn new_recursive() -> Self {
434    Self {
435      recursive: true,
436      ..Default::default()
437    }
438  }
439
440  #[inline]
441  pub fn recursive(&mut self) -> &mut Self {
442    self.recursive = true;
443    self
444  }
445
446  #[inline]
447  pub fn mode(&mut self, mode: u32) -> &mut Self {
448    self.mode = Some(mode);
449    self
450  }
451}
452
453pub trait BaseFsCreateDir {
454  #[doc(hidden)]
455  fn base_fs_create_dir(
456    &self,
457    path: &Path,
458    options: &CreateDirOptions,
459  ) -> io::Result<()>;
460}
461
462pub trait FsCreateDir: BaseFsCreateDir {
463  fn fs_create_dir(
464    &self,
465    path: impl AsRef<Path>,
466    options: &CreateDirOptions,
467  ) -> io::Result<()> {
468    self.base_fs_create_dir(path.as_ref(), options)
469  }
470}
471
472impl<T: BaseFsCreateDir> FsCreateDir for T {}
473
474// == FsCreateDirAll ==
475
476pub trait FsCreateDirAll: BaseFsCreateDir {
477  fn fs_create_dir_all(&self, path: impl AsRef<Path>) -> io::Result<()> {
478    self.base_fs_create_dir(
479      path.as_ref(),
480      &CreateDirOptions {
481        recursive: true,
482        mode: None,
483      },
484    )
485  }
486}
487
488impl<T: BaseFsCreateDir> FsCreateDirAll for T {}
489
490// == FsHardLink ==
491
492pub trait BaseFsHardLink {
493  #[doc(hidden)]
494  fn base_fs_hard_link(&self, src: &Path, dst: &Path) -> io::Result<()>;
495}
496
497pub trait FsHardLink: BaseFsHardLink {
498  fn fs_hard_link(
499    &self,
500    src: impl AsRef<Path>,
501    dst: impl AsRef<Path>,
502  ) -> io::Result<()> {
503    self.base_fs_hard_link(src.as_ref(), dst.as_ref())
504  }
505}
506
507impl<T: BaseFsHardLink> FsHardLink for T {}
508
509// == FsCreateJunction ==
510
511pub trait BaseFsCreateJunction {
512  #[doc(hidden)]
513  fn base_fs_create_junction(
514    &self,
515    original: &Path,
516    junction: &Path,
517  ) -> io::Result<()>;
518}
519
520pub trait FsCreateJunction: BaseFsCreateJunction {
521  /// Creates an NTFS junction.
522  fn fs_create_junction(
523    &self,
524    original: impl AsRef<Path>,
525    junction: impl AsRef<Path>,
526  ) -> io::Result<()> {
527    self.base_fs_create_junction(original.as_ref(), junction.as_ref())
528  }
529}
530
531impl<T: BaseFsCreateJunction> FsCreateJunction for T {}
532
533// == FsMetadata ==
534
535#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
536pub enum FileType {
537  File,
538  Dir,
539  Symlink,
540  Unknown,
541}
542
543impl FileType {
544  pub fn is_dir(&self) -> bool {
545    *self == Self::Dir
546  }
547
548  pub fn is_file(&self) -> bool {
549    *self == Self::File
550  }
551
552  pub fn is_symlink(&self) -> bool {
553    *self == Self::Symlink
554  }
555}
556
557impl From<std::fs::FileType> for FileType {
558  fn from(file_type: std::fs::FileType) -> Self {
559    if file_type.is_file() {
560      FileType::File
561    } else if file_type.is_dir() {
562      FileType::Dir
563    } else if file_type.is_symlink() {
564      FileType::Symlink
565    } else {
566      FileType::Unknown
567    }
568  }
569}
570
571#[allow(clippy::len_without_is_empty)]
572pub trait FsMetadataValue: std::fmt::Debug {
573  fn file_type(&self) -> FileType;
574  fn len(&self) -> u64;
575  fn accessed(&self) -> io::Result<SystemTime>;
576  fn created(&self) -> io::Result<SystemTime>;
577  fn changed(&self) -> io::Result<SystemTime>;
578  fn modified(&self) -> io::Result<SystemTime>;
579  fn dev(&self) -> io::Result<u64>;
580  fn ino(&self) -> io::Result<u64>;
581  fn mode(&self) -> io::Result<u32>;
582  fn nlink(&self) -> io::Result<u64>;
583  fn uid(&self) -> io::Result<u32>;
584  fn gid(&self) -> io::Result<u32>;
585  fn rdev(&self) -> io::Result<u64>;
586  fn blksize(&self) -> io::Result<u64>;
587  fn blocks(&self) -> io::Result<u64>;
588  fn is_block_device(&self) -> io::Result<bool>;
589  fn is_char_device(&self) -> io::Result<bool>;
590  fn is_fifo(&self) -> io::Result<bool>;
591  fn is_socket(&self) -> io::Result<bool>;
592  fn file_attributes(&self) -> io::Result<u32>;
593}
594
595pub trait BaseFsMetadata {
596  type Metadata: FsMetadataValue;
597
598  #[doc(hidden)]
599  fn base_fs_metadata(&self, path: &Path) -> io::Result<Self::Metadata>;
600
601  #[doc(hidden)]
602  fn base_fs_symlink_metadata(&self, path: &Path)
603    -> io::Result<Self::Metadata>;
604
605  #[doc(hidden)]
606  fn base_fs_exists(&self, path: &Path) -> io::Result<bool> {
607    match self.base_fs_symlink_metadata(path) {
608      Ok(_) => Ok(true),
609      Err(err) => {
610        if err.kind() == ErrorKind::NotFound {
611          Ok(false)
612        } else {
613          Err(err)
614        }
615      }
616    }
617  }
618
619  #[doc(hidden)]
620  fn base_fs_exists_no_err(&self, path: &Path) -> bool {
621    self.base_fs_exists(path).unwrap_or(false)
622  }
623}
624
625/// These two functions are so cloesly related that it becomes verbose to
626/// separate them out into two traits.
627pub trait FsMetadata: BaseFsMetadata {
628  #[inline]
629  fn fs_metadata(&self, path: impl AsRef<Path>) -> io::Result<Self::Metadata> {
630    self.base_fs_metadata(path.as_ref())
631  }
632
633  #[inline]
634  fn fs_symlink_metadata(
635    &self,
636    path: impl AsRef<Path>,
637  ) -> io::Result<Self::Metadata> {
638    self.base_fs_symlink_metadata(path.as_ref())
639  }
640
641  #[inline]
642  fn fs_is_file(&self, path: impl AsRef<Path>) -> io::Result<bool> {
643    Ok(self.fs_metadata(path)?.file_type() == FileType::File)
644  }
645
646  #[inline]
647  fn fs_is_file_no_err(&self, path: impl AsRef<Path>) -> bool {
648    self.fs_is_file(path).unwrap_or(false)
649  }
650
651  #[inline]
652  fn fs_is_dir(&self, path: impl AsRef<Path>) -> io::Result<bool> {
653    Ok(self.fs_metadata(path)?.file_type() == FileType::Dir)
654  }
655
656  #[inline]
657  fn fs_is_dir_no_err(&self, path: impl AsRef<Path>) -> bool {
658    self.fs_is_dir(path).unwrap_or(false)
659  }
660
661  #[inline]
662  fn fs_exists(&self, path: impl AsRef<Path>) -> io::Result<bool> {
663    self.base_fs_exists(path.as_ref())
664  }
665
666  #[inline]
667  fn fs_exists_no_err(&self, path: impl AsRef<Path>) -> bool {
668    self.base_fs_exists_no_err(path.as_ref())
669  }
670
671  #[inline]
672  fn fs_is_symlink(&self, path: impl AsRef<Path>) -> io::Result<bool> {
673    Ok(self.fs_symlink_metadata(path)?.file_type() == FileType::Symlink)
674  }
675
676  #[inline]
677  fn fs_is_symlink_no_err(&self, path: impl AsRef<Path>) -> bool {
678    self.fs_is_symlink(path).unwrap_or(false)
679  }
680}
681
682impl<T: BaseFsMetadata> FsMetadata for T {}
683
684// == FsOpen ==
685
686pub trait FsFile:
687  std::io::Read
688  + std::io::Write
689  + std::io::Seek
690  + FsFileIsTerminal
691  + FsFileLock
692  + FsFileMetadata
693  + FsFileSetPermissions
694  + FsFileSetTimes
695  + FsFileSetLen
696  + FsFileSyncAll
697  + FsFileSyncData
698  + FsFileAsRaw
699{
700}
701
702pub trait BoxableFsFile: Sized {
703  fn into_boxed(self) -> BoxedFsFile;
704}
705
706impl<T> BoxableFsFile for T
707where
708  T: FsFile + Sized + 'static,
709{
710  fn into_boxed(self) -> BoxedFsFile {
711    BoxedFsFile(Box::new(self))
712  }
713}
714
715pub trait BaseFsOpen {
716  // ideally this wouldn't be constrained, but by not doing
717  // this then the type parameters get really out of hand
718  type File: FsFile + Sized + 'static;
719
720  #[doc(hidden)]
721  fn base_fs_open(
722    &self,
723    path: &Path,
724    options: &OpenOptions,
725  ) -> io::Result<Self::File>;
726}
727
728pub trait FsOpen: BaseFsOpen {
729  #[inline]
730  fn fs_open(
731    &self,
732    path: impl AsRef<Path>,
733    options: &OpenOptions,
734  ) -> io::Result<Self::File> {
735    self.base_fs_open(path.as_ref(), options)
736  }
737}
738
739impl<T: BaseFsOpen> FsOpen for T {}
740
741// == FsRead ==
742
743pub trait BaseFsRead {
744  #[doc(hidden)]
745  fn base_fs_read(&self, path: &Path) -> io::Result<Cow<'static, [u8]>>;
746}
747
748pub trait FsRead: BaseFsRead {
749  #[inline]
750  fn fs_read(&self, path: impl AsRef<Path>) -> io::Result<Cow<'static, [u8]>> {
751    self.base_fs_read(path.as_ref())
752  }
753
754  fn fs_read_to_string(
755    &self,
756    path: impl AsRef<Path>,
757  ) -> io::Result<Cow<'static, str>> {
758    let bytes = self.fs_read(path)?;
759    match bytes {
760      Cow::Borrowed(bytes) => str::from_utf8(bytes)
761        .map(Cow::Borrowed)
762        .map_err(|e| e.to_string()),
763      Cow::Owned(bytes) => String::from_utf8(bytes)
764        .map(Cow::Owned)
765        .map_err(|e| e.to_string()),
766    }
767    .map_err(|error_text| Error::new(ErrorKind::InvalidData, error_text))
768  }
769
770  fn fs_read_to_string_lossy(
771    &self,
772    path: impl AsRef<Path>,
773  ) -> io::Result<Cow<'static, str>> {
774    // Like String::from_utf8_lossy but operates on owned values
775    #[inline(always)]
776    fn string_from_utf8_lossy(buf: Vec<u8>) -> String {
777      match String::from_utf8_lossy(&buf) {
778        // buf contained non-utf8 chars than have been patched
779        Cow::Owned(s) => s,
780        // SAFETY: if Borrowed then the buf only contains utf8 chars,
781        // we do this instead of .into_owned() to avoid copying the input buf
782        Cow::Borrowed(_) => unsafe { String::from_utf8_unchecked(buf) },
783      }
784    }
785
786    let bytes = self.fs_read(path)?;
787    match bytes {
788      Cow::Borrowed(bytes) => Ok(String::from_utf8_lossy(bytes)),
789      Cow::Owned(bytes) => Ok(Cow::Owned(string_from_utf8_lossy(bytes))),
790    }
791  }
792}
793
794impl<T: BaseFsRead> FsRead for T {}
795
796// == FsReadDir ==
797
798pub trait FsDirEntry: std::fmt::Debug {
799  type Metadata: FsMetadataValue;
800
801  fn file_name(&self) -> Cow<'_, OsStr>;
802  fn file_type(&self) -> io::Result<FileType>;
803  fn metadata(&self) -> io::Result<Self::Metadata>;
804  fn path(&self) -> Cow<'_, Path>;
805}
806
807pub trait BaseFsReadDir {
808  type ReadDirEntry: FsDirEntry + 'static;
809
810  #[doc(hidden)]
811  fn base_fs_read_dir(
812    &self,
813    path: &Path,
814  ) -> io::Result<Box<dyn Iterator<Item = io::Result<Self::ReadDirEntry>>>>;
815}
816
817pub trait FsReadDir: BaseFsReadDir {
818  #[inline]
819  fn fs_read_dir(
820    &self,
821    path: impl AsRef<Path>,
822  ) -> io::Result<Box<dyn Iterator<Item = io::Result<Self::ReadDirEntry>>>> {
823    self.base_fs_read_dir(path.as_ref())
824  }
825}
826
827impl<T: BaseFsReadDir> FsReadDir for T {}
828
829// == FsReadLink ==
830
831pub trait BaseFsReadLink {
832  #[doc(hidden)]
833  fn base_fs_read_link(&self, path: &Path) -> io::Result<PathBuf>;
834}
835
836pub trait FsReadLink: BaseFsReadLink {
837  #[inline]
838  fn fs_read_link(&self, path: impl AsRef<Path>) -> io::Result<PathBuf> {
839    self.base_fs_read_link(path.as_ref())
840  }
841}
842
843impl<T: BaseFsReadLink> FsReadLink for T {}
844
845// == FsRemoveDir ==
846
847pub trait BaseFsRemoveDir {
848  #[doc(hidden)]
849  fn base_fs_remove_dir(&self, path: &Path) -> io::Result<()>;
850}
851
852pub trait FsRemoveDir: BaseFsRemoveDir {
853  #[inline]
854  fn fs_remove_dir(&self, path: impl AsRef<Path>) -> io::Result<()> {
855    self.base_fs_remove_dir(path.as_ref())
856  }
857}
858
859impl<T: BaseFsRemoveDir> FsRemoveDir for T {}
860
861// == FsRemoveDirAll ==
862
863pub trait BaseFsRemoveDirAll {
864  #[doc(hidden)]
865  fn base_fs_remove_dir_all(&self, path: &Path) -> io::Result<()>;
866}
867
868pub trait FsRemoveDirAll: BaseFsRemoveDirAll {
869  #[inline]
870  fn fs_remove_dir_all(&self, path: impl AsRef<Path>) -> io::Result<()> {
871    self.base_fs_remove_dir_all(path.as_ref())
872  }
873}
874
875impl<T: BaseFsRemoveDirAll> FsRemoveDirAll for T {}
876
877// == FsRemoveFile ==
878
879pub trait BaseFsRemoveFile {
880  #[doc(hidden)]
881  fn base_fs_remove_file(&self, path: &Path) -> io::Result<()>;
882}
883
884pub trait FsRemoveFile: BaseFsRemoveFile {
885  #[inline]
886  fn fs_remove_file(&self, path: impl AsRef<Path>) -> io::Result<()> {
887    self.base_fs_remove_file(path.as_ref())
888  }
889}
890
891impl<T: BaseFsRemoveFile> FsRemoveFile for T {}
892
893// == FsRename ==
894
895pub trait BaseFsRename {
896  #[doc(hidden)]
897  fn base_fs_rename(&self, from: &Path, to: &Path) -> io::Result<()>;
898}
899
900pub trait FsRename: BaseFsRename {
901  #[inline]
902  fn fs_rename(
903    &self,
904    from: impl AsRef<Path>,
905    to: impl AsRef<Path>,
906  ) -> io::Result<()> {
907    self.base_fs_rename(from.as_ref(), to.as_ref())
908  }
909}
910
911impl<T: BaseFsRename> FsRename for T {}
912
913// == FsSetFileTimes ==
914
915pub trait BaseFsSetFileTimes {
916  #[doc(hidden)]
917  fn base_fs_set_file_times(
918    &self,
919    path: &Path,
920    atime: SystemTime,
921    mtime: SystemTime,
922  ) -> io::Result<()>;
923}
924
925pub trait FsSetFileTimes: BaseFsSetFileTimes {
926  #[inline]
927  fn fs_set_file_times(
928    &self,
929    path: impl AsRef<Path>,
930    atime: SystemTime,
931    mtime: SystemTime,
932  ) -> io::Result<()> {
933    self.base_fs_set_file_times(path.as_ref(), atime, mtime)
934  }
935}
936
937impl<T: BaseFsSetFileTimes> FsSetFileTimes for T {}
938
939// == FsSetSymlinkFileTimes ==
940
941pub trait BaseFsSetSymlinkFileTimes {
942  #[doc(hidden)]
943  fn base_fs_set_symlink_file_times(
944    &self,
945    path: &Path,
946    atime: SystemTime,
947    mtime: SystemTime,
948  ) -> io::Result<()>;
949}
950
951pub trait FsSetSymlinkFileTimes: BaseFsSetSymlinkFileTimes {
952  #[inline]
953  fn fs_set_symlink_file_times(
954    &self,
955    path: impl AsRef<Path>,
956    atime: SystemTime,
957    mtime: SystemTime,
958  ) -> io::Result<()> {
959    self.base_fs_set_symlink_file_times(path.as_ref(), atime, mtime)
960  }
961}
962
963impl<T: BaseFsSetSymlinkFileTimes> FsSetSymlinkFileTimes for T {}
964
965// == FsSetPermissions ==
966
967pub trait BaseFsSetPermissions {
968  #[doc(hidden)]
969  fn base_fs_set_permissions(&self, path: &Path, mode: u32) -> io::Result<()>;
970}
971
972pub trait FsSetPermissions: BaseFsSetPermissions {
973  fn fs_set_permissions(
974    &self,
975    path: impl AsRef<Path>,
976    mode: u32,
977  ) -> io::Result<()> {
978    self.base_fs_set_permissions(path.as_ref(), mode)
979  }
980}
981
982impl<T: BaseFsSetPermissions> FsSetPermissions for T {}
983
984// == FsSymlinkDir ==
985
986pub trait BaseFsSymlinkDir {
987  #[doc(hidden)]
988  fn base_fs_symlink_dir(&self, original: &Path, link: &Path)
989    -> io::Result<()>;
990}
991
992pub trait FsSymlinkDir: BaseFsSymlinkDir {
993  #[inline]
994  fn fs_symlink_dir(
995    &self,
996    original: impl AsRef<Path>,
997    link: impl AsRef<Path>,
998  ) -> io::Result<()> {
999    self.base_fs_symlink_dir(original.as_ref(), link.as_ref())
1000  }
1001}
1002
1003impl<T: BaseFsSymlinkDir> FsSymlinkDir for T {}
1004
1005// == FsSymlinkFile ==
1006
1007pub trait BaseFsSymlinkFile {
1008  #[doc(hidden)]
1009  fn base_fs_symlink_file(
1010    &self,
1011    original: &Path,
1012    link: &Path,
1013  ) -> io::Result<()>;
1014}
1015
1016pub trait FsSymlinkFile: BaseFsSymlinkFile {
1017  #[inline]
1018  fn fs_symlink_file(
1019    &self,
1020    original: impl AsRef<Path>,
1021    link: impl AsRef<Path>,
1022  ) -> io::Result<()> {
1023    self.base_fs_symlink_file(original.as_ref(), link.as_ref())
1024  }
1025}
1026
1027impl<T: BaseFsSymlinkFile> FsSymlinkFile for T {}
1028
1029// == FsWrite ==
1030
1031pub trait BaseFsWrite {
1032  #[doc(hidden)]
1033  fn base_fs_write(&self, path: &Path, data: &[u8]) -> io::Result<()>;
1034}
1035
1036pub trait FsWrite: BaseFsWrite {
1037  #[inline]
1038  fn fs_write(
1039    &self,
1040    path: impl AsRef<Path>,
1041    data: impl AsRef<[u8]>,
1042  ) -> io::Result<()> {
1043    self.base_fs_write(path.as_ref(), data.as_ref())
1044  }
1045}
1046
1047impl<T: BaseFsWrite> FsWrite for T {}
1048
1049// #### FILE SYSTEM FILE ####
1050
1051pub trait FsFileAsRaw {
1052  /// Returns the raw handle for a file on Windows platforms only
1053  /// or `None` when the file doesn't support it (ex. in-memory file system).
1054  #[cfg(windows)]
1055  fn fs_file_as_raw_handle(&self) -> Option<std::os::windows::io::RawHandle>;
1056
1057  /// Returns the raw file descriptor on Unix platforms only
1058  /// or `None` when the file doesn't support it (ex. in-memory file system).
1059  #[cfg(unix)]
1060  fn fs_file_as_raw_fd(&self) -> Option<std::os::fd::RawFd>;
1061}
1062
1063pub trait FsFileIsTerminal {
1064  fn fs_file_is_terminal(&self) -> bool;
1065}
1066
1067pub enum FsFileLockMode {
1068  Shared,
1069  Exclusive,
1070}
1071
1072pub trait FsFileLock {
1073  fn fs_file_lock(&mut self, mode: FsFileLockMode) -> io::Result<()>;
1074  fn fs_file_try_lock(&mut self, mode: FsFileLockMode) -> io::Result<()>;
1075  fn fs_file_unlock(&mut self) -> io::Result<()>;
1076}
1077
1078pub trait FsFileMetadata {
1079  /// Gets the file metadata.
1080  ///
1081  /// This is boxed because I couldn't figure out how to do
1082  /// this well with type parameters.
1083  fn fs_file_metadata(&self) -> io::Result<BoxedFsMetadataValue>;
1084}
1085
1086pub trait FsFileSetLen {
1087  fn fs_file_set_len(&mut self, size: u64) -> io::Result<()>;
1088}
1089
1090pub trait FsFileSetPermissions {
1091  fn fs_file_set_permissions(&mut self, mode: u32) -> io::Result<()>;
1092}
1093
1094#[derive(Debug, Clone, Default)]
1095pub struct FsFileTimes {
1096  pub accessed: Option<SystemTime>,
1097  pub modified: Option<SystemTime>,
1098}
1099
1100impl FsFileTimes {
1101  pub fn new() -> Self {
1102    Self::default()
1103  }
1104
1105  pub fn accessed(&mut self, accessed: SystemTime) -> &mut Self {
1106    self.accessed = Some(accessed);
1107    self
1108  }
1109
1110  pub fn modified(&mut self, accessed: SystemTime) -> &mut Self {
1111    self.modified = Some(accessed);
1112    self
1113  }
1114}
1115
1116pub trait FsFileSetTimes {
1117  fn fs_file_set_times(&mut self, times: FsFileTimes) -> io::Result<()>;
1118}
1119
1120pub trait FsFileSyncAll {
1121  fn fs_file_sync_all(&mut self) -> io::Result<()>;
1122}
1123
1124pub trait FsFileSyncData {
1125  fn fs_file_sync_data(&mut self) -> io::Result<()>;
1126}
1127
1128// #### SYSTEM ####
1129
1130pub trait SystemTimeNow {
1131  fn sys_time_now(&self) -> std::time::SystemTime;
1132}
1133
1134pub trait SystemRandom {
1135  fn sys_random(&self, buf: &mut [u8]) -> io::Result<()>;
1136
1137  fn sys_random_u8(&self) -> io::Result<u8> {
1138    let mut buf = [0; 1];
1139    self.sys_random(&mut buf)?;
1140    Ok(buf[0])
1141  }
1142
1143  fn sys_random_u32(&self) -> io::Result<u32> {
1144    let mut buf = [0; 4];
1145    self.sys_random(&mut buf)?;
1146    Ok(u32::from_le_bytes(buf))
1147  }
1148
1149  fn sys_random_u64(&self) -> io::Result<u64> {
1150    let mut buf = [0; 8];
1151    self.sys_random(&mut buf)?;
1152    Ok(u64::from_le_bytes(buf))
1153  }
1154}
1155
1156pub trait ThreadSleep {
1157  fn thread_sleep(&self, duration: std::time::Duration);
1158}