safa_abi/
errors.rs

1use core::fmt::Debug;
2
3#[allow(dead_code)]
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[repr(u16)]
6pub enum ErrorStatus {
7    /// Use when no ErrorStatus is available for xyz and you cannot add a new one
8    Generic = 1,
9    OperationNotSupported = 2,
10    /// For example an elf class is not supported, there is a difference between NotSupported and
11    /// OperationNotSupported
12    NotSupported = 3,
13    /// For example a magic value is invalid
14    Corrupted = 4,
15    InvalidSyscall = 5,
16    /// There is no Resource associated with a given ID
17    UnknownResource = 6,
18    InvalidPid = 7,
19    InvalidOffset = 8,
20    /// instead of panicking syscalls will return this on null and unaligned pointers
21    /// some operations may accept null pointers
22    InvalidPtr = 9,
23    /// for operations that requires a valid utf8 str...
24    InvalidStr = 0xA,
25    /// for operations that requires a str that doesn't exceed a max length such as
26    /// file names (128 bytes)
27    StrTooLong = 0xB,
28    InvalidPath = 0xC,
29    NoSuchAFileOrDirectory = 0xD,
30    NotAFile = 0xE,
31    NotADirectory = 0xF,
32    AlreadyExists = 0x10,
33    NotExecutable = 0x11,
34    DirectoryNotEmpty = 0x12,
35    /// Generic permissions(protection) related error
36    MissingPermissions = 0x13,
37    /// Memory Mapping error for now this means that the region has been already mapped before
38    MMapError = 0x14,
39    Busy = 0x15,
40    // Errors sent by processes
41    NotEnoughArguments = 0x16,
42    OutOfMemory = 0x17,
43    /// Invalid Thread ID
44    InvalidTid = 0x18,
45    /// Operation Timeouted
46    Timeout = 0x19,
47    /// A given Command is unknown or invalid
48    InvalidCommand = 0x1A,
49    /// A given Argument is invalid
50    InvalidArgument = 0x1B,
51    /// For example happens when a new error is added here, returned by a syscall and the Userspace App isn't aware of what that error means because it's ABI is out of date
52    Unknown = 0x1C,
53    /// A panick or a fatal exception occurred, used for example when the rust runtime panics and it wants to exit the process with a value
54    Panic = 0x1D,
55    /// A given resource wasn't a Device while one was expected
56    NotADevice = 0x1E,
57    /// An Operation on a resource would block while it was configured as not blockable, for example through sockets
58    WouldBlock = 0x1F,
59    /// A bi-directional Connection closed from a side and not the other
60    ConnectionClosed = 0x20,
61    /// Attempt to form a Connection failed
62    ConnectionRefused = 0x21,
63    /// There is a resource associated with the given ID but it isn't supported by that operation
64    UnsupportedResource = 0x22,
65    /// The given Resource is not duplictable
66    ResourceCloneFailed = 0x23,
67    /// A given X is incompatible with a Y in an operation that requires them to be compatible
68    ///
69    /// Used for example with Sockets when you try to connect with a bad Descriptor
70    TypeMismatch = 0x24,
71    /// A given buffer is too short to hold full information, this for example is used with the keyboard input event device to indicate that we couldn't fit an event in a buffer
72    TooShort = 0x25,
73    /// Failed to connect to an address because it wasn't found
74    AddressNotFound = 0x26,
75    /// A given input buffer size is not acceptable by the attempted operation
76    InvalidSize = 0x27,
77    /// The syscall was interrupted by something, such as a kill command.
78    ForceTerminated = 0x28,
79    /// Attempt to use an address thats already used.
80    AddressAlreadyInUse = 0x29,
81    /// Attempt to use an interface thats not bound to an address.
82    NotBound = 0x2A,
83}
84
85impl ErrorStatus {
86    // update when a new error is added
87    const MAX: u16 = Self::NotBound as u16;
88
89    #[inline(always)]
90    /// Gives a string description of the error
91    pub fn as_str(&self) -> &'static str {
92        use ErrorStatus::*;
93        match *self {
94            InvalidSize => "Invalid Size",
95            AddressNotFound => "Address Not Found",
96            TooShort => "Too Short",
97            Generic => "Generic Error",
98            OperationNotSupported => "Operation Not Supported",
99            NotSupported => "Object Not Supported",
100            Corrupted => "Corrupted",
101            InvalidSyscall => "Invalid Syscall",
102            UnknownResource => "Unknown Resource ID",
103            UnsupportedResource => "Resource not supported by that Operation",
104            ResourceCloneFailed => "Failed to clone Resource",
105            TypeMismatch => "Type Mismatch",
106            InvalidPid => "Invalid PID",
107            InvalidTid => "Invalid TID",
108            InvalidOffset => "Invalid Offset",
109            InvalidPtr => "Invalid Ptr (not aligned or null)",
110            InvalidStr => "Invalid Str (not utf8)",
111            StrTooLong => "Str too Long",
112            InvalidPath => "Invalid Path",
113            NoSuchAFileOrDirectory => "No Such a File or Directory",
114            NotAFile => "Not a File",
115            NotADirectory => "Not a Directory",
116            AlreadyExists => "Already Exists",
117            NotExecutable => "Not Executable",
118            DirectoryNotEmpty => "Directory not Empty",
119            MissingPermissions => "Missing Permissions",
120            MMapError => "Memory Map Error (most likely out of memory)",
121            Busy => "Resource Busy",
122            NotEnoughArguments => "Not Enough Arguments",
123            OutOfMemory => "Out of Memory",
124            InvalidArgument => "Invalid Argument",
125            InvalidCommand => "Invalid Command",
126            Unknown => "Operation Unknown",
127            Panic => "Unrecoverable Panick",
128            Timeout => "Operation Timeouted",
129            NotADevice => "Not A Device",
130            ConnectionClosed => "Connection Closed",
131            ConnectionRefused => "Connection Refused",
132            WouldBlock => "Operation Would Block",
133            ForceTerminated => "Operation Terminated",
134            AddressAlreadyInUse => "Address Already In Use",
135            NotBound => "Interface Not Bound",
136        }
137    }
138
139    /// Try to convert a given `value` into an error code
140    pub const fn try_from_u16(value: u16) -> Result<Self, ()> {
141        if value > 0 && value <= Self::MAX {
142            Ok(unsafe { core::mem::transmute(value) })
143        } else {
144            Err(())
145        }
146    }
147    /// Converts a given `value` into an error code on failure, returns [`Self::Unknown`]
148    pub const fn from_u16(value: u16) -> Self {
149        /* const hack instead of unwrap_or */
150        match Self::try_from_u16(value) {
151            Ok(k) => k,
152            Err(()) => Self::Unknown,
153        }
154    }
155}
156
157impl TryFrom<u16> for ErrorStatus {
158    type Error = ();
159
160    fn try_from(value: u16) -> Result<Self, Self::Error> {
161        Self::try_from_u16(value)
162    }
163}
164
165/// Represents the results of a SafaOS syscall, either an [`ErrorStatus`] or an Ok [`usize`] value smaller than or equal to [`isize::MAX`].
166#[derive(Debug, Clone, Copy, PartialEq, Eq)]
167#[repr(transparent)]
168pub struct SysResult(isize);
169
170impl SysResult {
171    /// Converts an err into a [`SysResult`].
172    pub const fn err(err: ErrorStatus) -> Self {
173        Self(-(err as isize))
174    }
175
176    /// Turns a [`SysResult`] into result.
177    pub const fn into_result(self) -> Result<usize, ErrorStatus> {
178        if self.0.is_negative() {
179            Err(ErrorStatus::from_u16((-self.0) as u16))
180        } else {
181            Ok(self.0 as usize)
182        }
183    }
184
185    /// Attempts to convert a result into [`SysResult`], returns an error if the value is Ok(x) and x is larger than [`isize::MAX`]
186    pub const fn try_from_result(result: Result<usize, ErrorStatus>) -> Result<Self, ()> {
187        match result {
188            Err(err) => Ok(Self::err(err)),
189            Ok(value) => Self::try_ok(value),
190        }
191    }
192
193    /// Converts an Ok value [`value`] into [`Self`], it is expected to not be larger than [`isize::MAX`] or it panicks.
194    pub const fn ok(value: usize) -> Self {
195        let Ok(ok) = Self::try_ok(value) else {
196            panic!("Attempt to construct an Ok SysResult value larger than isize::MAX")
197        };
198        ok
199    }
200
201    /// Tries to convert an Ok value [`value`] into [`SysResult`], return an error if the value is larger than [`isize::MAX`].
202    pub const fn try_ok(value: usize) -> Result<Self, ()> {
203        let value = value as isize;
204        if value.is_negative() {
205            return Err(());
206        }
207
208        Ok(Self(value))
209    }
210
211    /// Converts a [`SysResult`] into an isize, negative value is for an error, use [`Self::into_result`] instead.
212    pub const fn as_isize(&self) -> isize {
213        self.0
214    }
215
216    /// Converts an isize into [`SysResult`]
217    /// # Safety
218    /// Perefectly safe as this type doesn't guarantee the contained error value (negative value) is valid.
219    pub const fn from_isize(isize: isize) -> Self {
220        Self(isize)
221    }
222}
223
224impl From<ErrorStatus> for SysResult {
225    #[inline(always)]
226    fn from(value: ErrorStatus) -> Self {
227        Self::err(value)
228    }
229}
230
231impl From<Result<usize, ErrorStatus>> for SysResult {
232    /// Panicks if the results is an Ok value larger than isize::MAX
233    #[inline(always)]
234    fn from(value: Result<usize, ErrorStatus>) -> Self {
235        Self::try_from_result(value).expect("Ok value is bigger than isize::MAX")
236    }
237}
238
239impl From<SysResult> for Result<usize, ErrorStatus> {
240    #[inline(always)]
241    fn from(value: SysResult) -> Self {
242        value.into_result()
243    }
244}
245
246impl Into<isize> for SysResult {
247    #[inline(always)]
248    fn into(self) -> isize {
249        self.0
250    }
251}
252
253impl From<isize> for SysResult {
254    fn from(value: isize) -> Self {
255        Self::from_isize(value)
256    }
257}
258
259pub trait IntoErr {
260    fn into_err(self) -> ErrorStatus;
261}
262
263impl<T: IntoErr> From<T> for ErrorStatus {
264    fn from(value: T) -> Self {
265        value.into_err()
266    }
267}
268
269impl From<core::str::Utf8Error> for ErrorStatus {
270    fn from(value: core::str::Utf8Error) -> Self {
271        match value {
272            core::str::Utf8Error { .. } => Self::InvalidStr,
273        }
274    }
275}
276
277#[cfg(feature = "std")]
278mod std_only {
279    use super::SysResult;
280    use std::process::ExitCode;
281    use std::process::Termination;
282    impl Termination for SysResult {
283        fn report(self) -> ExitCode {
284            let u16: u16 = match self.into_result() {
285                Ok(_) => 0,
286                Err(smth) => smth as u16,
287            };
288            ExitCode::from(u16 as u8)
289        }
290    }
291}