1#![feature(let_chains)]
2#![feature(dir_entry_ext2)]
3#![allow(clippy::missing_errors_doc)]
4
5use std::{
6 fmt::{Debug, Display},
7 io,
8 path::PathBuf,
9};
10
11use error_stack::{Result, ResultExt};
12pub use run::run;
13pub use sessions::{
14 delete as delete_sessions, list as list_sessions, stop as stop_sessions, Op as SessionOperand,
15};
16
17mod run;
18mod sessions;
19
20#[derive(thiserror::Error, Debug)]
21pub enum Error {
22 #[error("An IO error occurred.")]
23 Io,
24 #[error("Invalid argument.")]
25 InvalidArgument,
26 #[error("ForkFS must be run as root.")]
27 NotRoot,
28 #[error("Session not found.")]
29 SessionNotFound,
30 #[error("Setup required.")]
31 SetupRequired,
32}
33
34fn get_sessions_dir() -> PathBuf {
35 let mut sessions_dir = dirs::cache_dir().unwrap_or_else(|| PathBuf::from("/tmp"));
36 sessions_dir.push("forkfs");
37 sessions_dir
38}
39
40trait IoErr<Out> {
41 fn map_io_err_lazy<P: Display + Debug + Send + Sync + 'static>(
42 self,
43 f: impl FnOnce() -> P,
44 ) -> Out;
45
46 fn map_io_err<P: Display + Debug + Send + Sync + 'static>(self, p: P) -> Out;
47}
48
49impl<T> IoErr<Result<T, Error>> for io::Result<T> {
50 fn map_io_err_lazy<P: Display + Debug + Send + Sync + 'static>(
51 self,
52 f: impl FnOnce() -> P,
53 ) -> Result<T, Error> {
54 self.attach_printable_lazy(f).change_context(Error::Io)
55 }
56
57 fn map_io_err<P: Display + Debug + Send + Sync + 'static>(self, p: P) -> Result<T, Error> {
58 self.attach_printable(p).change_context(Error::Io)
59 }
60}
61
62impl<T> IoErr<Result<T, Error>> for std::result::Result<T, rustix::io::Errno> {
63 fn map_io_err_lazy<P: Display + Debug + Send + Sync + 'static>(
64 self,
65 f: impl FnOnce() -> P,
66 ) -> Result<T, Error> {
67 self.map_err(io::Error::from).map_io_err_lazy(f)
68 }
69
70 fn map_io_err<P: Display + Debug + Send + Sync + 'static>(self, p: P) -> Result<T, Error> {
71 self.map_err(io::Error::from).map_io_err(p)
72 }
73}
74
75mod path_undo {
76 use std::{
77 fmt::{Debug, Formatter},
78 ops::{Deref, DerefMut},
79 path::{Path, PathBuf},
80 };
81
82 pub struct TmpPath<'a>(&'a mut PathBuf);
83
84 impl<'a> TmpPath<'a> {
85 pub fn new(path: &'a mut PathBuf, child: impl AsRef<Path>) -> Self {
86 path.push(child);
87 Self(path)
88 }
89 }
90
91 impl<'a> Deref for TmpPath<'a> {
92 type Target = PathBuf;
93
94 fn deref(&self) -> &Self::Target {
95 self.0
96 }
97 }
98
99 impl<'a> AsRef<Path> for TmpPath<'a> {
100 fn as_ref(&self) -> &Path {
101 self.0
102 }
103 }
104
105 impl<'a> DerefMut for TmpPath<'a> {
106 fn deref_mut(&mut self) -> &mut Self::Target {
107 self.0
108 }
109 }
110
111 impl<'a> Debug for TmpPath<'a> {
112 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
113 Debug::fmt(&**self, f)
114 }
115 }
116
117 impl<'a> Drop for TmpPath<'a> {
118 fn drop(&mut self) {
119 self.pop();
120 }
121 }
122}