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