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