Files
Magisk/native/src/base/result.rs

342 lines
9.0 KiB
Rust
Raw Normal View History

use crate::logging::Formatter;
use crate::{LogLevel, errno, log_with_args, log_with_formatter};
2024-03-05 01:48:35 -08:00
use std::fmt::Display;
use std::panic::Location;
use std::ptr::NonNull;
use std::{fmt, io};
2024-03-05 01:48:35 -08:00
// Error handling throughout the Rust codebase in Magisk:
//
// All errors should be logged and consumed as soon as possible and converted into LoggedError.
// For `Result` with errors that implement the `Display` trait, use the `?` operator to
// log and convert to LoggedResult.
//
// To log an error with more information, use `ResultExt::log_with_msg()`.
#[derive(Default)]
pub struct LoggedError {}
pub type LoggedResult<T> = Result<T, LoggedError>;
#[macro_export]
macro_rules! log_err {
() => {{
Err($crate::LoggedError::default())
}};
2024-03-05 01:48:35 -08:00
($($args:tt)+) => {{
2025-05-30 11:11:36 -07:00
$crate::error!($($args)+);
Err($crate::LoggedError::default())
2024-03-05 01:48:35 -08:00
}};
}
// Any result or option can be silenced
pub trait SilentLogExt<T> {
2024-03-05 01:48:35 -08:00
fn silent(self) -> LoggedResult<T>;
}
impl<T, E> SilentLogExt<T> for Result<T, E> {
2024-03-05 01:48:35 -08:00
fn silent(self) -> LoggedResult<T> {
self.map_err(|_| LoggedError::default())
2024-03-05 01:48:35 -08:00
}
}
impl<T> SilentLogExt<T> for Option<T> {
2024-03-05 01:48:35 -08:00
fn silent(self) -> LoggedResult<T> {
self.ok_or_else(LoggedError::default)
2024-03-05 01:48:35 -08:00
}
}
// Public API for logging results
pub trait ResultExt<T> {
fn log(self) -> LoggedResult<T>;
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
2025-02-04 21:15:16 +08:00
fn log_ok(self);
2024-03-05 01:48:35 -08:00
}
// Public API for converting Option to LoggedResult
pub trait OptionExt<T> {
fn ok_or_log(self) -> LoggedResult<T>;
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T>;
}
impl<T> OptionExt<T> for Option<T> {
#[inline(always)]
fn ok_or_log(self) -> LoggedResult<T> {
self.ok_or_else(LoggedError::default)
}
#[cfg(not(debug_assertions))]
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
self.ok_or_else(|| {
do_log_msg(LogLevel::Error, None, f);
LoggedError::default()
})
}
#[track_caller]
#[cfg(debug_assertions)]
fn ok_or_log_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
let caller = Some(Location::caller());
self.ok_or_else(|| {
do_log_msg(LogLevel::Error, caller, f);
LoggedError::default()
})
}
}
trait Loggable {
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedError;
2024-03-05 01:48:35 -08:00
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
self,
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) -> LoggedError;
2024-03-05 01:48:35 -08:00
}
impl<T, E: Loggable> ResultExt<T> for Result<T, E> {
2024-03-05 01:48:35 -08:00
#[cfg(not(debug_assertions))]
fn log(self) -> LoggedResult<T> {
self.map_err(|e| e.do_log(LogLevel::Error, None))
2024-03-05 01:48:35 -08:00
}
#[track_caller]
#[cfg(debug_assertions)]
fn log(self) -> LoggedResult<T> {
let caller = Some(Location::caller());
self.map_err(|e| e.do_log(LogLevel::Error, caller))
2024-03-05 01:48:35 -08:00
}
2025-02-16 11:46:42 -08:00
#[cfg(not(debug_assertions))]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
self.map_err(|e| e.do_log_msg(LogLevel::Error, None, f))
2025-02-16 11:46:42 -08:00
}
2025-02-04 21:15:16 +08:00
#[track_caller]
#[cfg(debug_assertions)]
2025-02-16 11:46:42 -08:00
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
let caller = Some(Location::caller());
self.map_err(|e| e.do_log_msg(LogLevel::Error, caller, f))
2025-02-04 21:15:16 +08:00
}
2025-02-16 11:46:42 -08:00
2025-02-04 21:15:16 +08:00
#[cfg(not(debug_assertions))]
fn log_ok(self) {
self.map_err(|e| e.do_log(LogLevel::Error, None)).ok();
2025-02-04 21:15:16 +08:00
}
2024-03-05 01:48:35 -08:00
#[track_caller]
#[cfg(debug_assertions)]
2025-02-16 11:46:42 -08:00
fn log_ok(self) {
let caller = Some(Location::caller());
self.map_err(|e| e.do_log(LogLevel::Error, caller)).ok();
2024-03-05 01:48:35 -08:00
}
}
impl<T> ResultExt<T> for LoggedResult<T> {
fn log(self) -> LoggedResult<T> {
2024-03-05 01:48:35 -08:00
self
}
#[cfg(not(debug_assertions))]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
2025-08-23 04:17:01 -07:00
self.inspect_err(|_| do_log_msg(LogLevel::Error, None, f))
2024-03-05 01:48:35 -08:00
}
#[track_caller]
#[cfg(debug_assertions)]
fn log_with_msg<F: FnOnce(Formatter) -> fmt::Result>(self, f: F) -> LoggedResult<T> {
let caller = Some(Location::caller());
2025-08-23 04:17:01 -07:00
self.inspect_err(|_| do_log_msg(LogLevel::Error, caller, f))
2024-03-05 01:48:35 -08:00
}
fn log_ok(self) {}
2024-03-05 01:48:35 -08:00
}
// Allow converting Loggable errors to LoggedError to support `?` operator
impl<T: Loggable> From<T> for LoggedError {
2024-03-05 01:48:35 -08:00
#[cfg(not(debug_assertions))]
fn from(e: T) -> Self {
e.do_log(LogLevel::Error, None)
2024-03-05 01:48:35 -08:00
}
#[track_caller]
#[cfg(debug_assertions)]
fn from(e: T) -> Self {
let caller = Some(Location::caller());
e.do_log(LogLevel::Error, caller)
}
}
// Actual logging implementation
// Make all printable objects Loggable
impl<T: Display> Loggable for T {
fn do_log(self, level: LogLevel, caller: Option<&'static Location>) -> LoggedError {
if let Some(caller) = caller {
log_with_args!(level, "[{}:{}] {:#}", caller.file(), caller.line(), self);
} else {
log_with_args!(level, "{:#}", self);
}
LoggedError::default()
}
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
self,
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) -> LoggedError {
log_with_formatter(level, |w| {
if let Some(caller) = caller {
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
}
f(w)?;
writeln!(w, ": {self:#}")
});
2024-03-05 01:48:35 -08:00
LoggedError::default()
}
}
fn do_log_msg<F: FnOnce(Formatter) -> fmt::Result>(
level: LogLevel,
caller: Option<&'static Location>,
f: F,
) {
log_with_formatter(level, |w| {
if let Some(caller) = caller {
write!(w, "[{}:{}] ", caller.file(), caller.line())?;
}
f(w)?;
w.write_char('\n')
});
}
// Check libc return value and map to Result
pub trait LibcReturn
where
2025-09-08 10:55:57 -07:00
Self: Sized,
{
type Value;
2025-09-08 10:55:57 -07:00
fn into_result(self) -> Result<Self::Value, i32>;
2025-09-08 10:55:57 -07:00
fn into_os_result<'a>(
self,
name: &'static str,
arg1: Option<&'a str>,
arg2: Option<&'a str>,
) -> OsResult<'a, Self::Value> {
2025-09-08 10:55:57 -07:00
self.into_result()
.map_err(|e| OsError::new(e, name, arg1, arg2))
}
fn check_os_err<'a>(
self,
name: &'static str,
arg1: Option<&'a str>,
arg2: Option<&'a str>,
) -> OsResult<'a, ()> {
2025-09-08 10:55:57 -07:00
self.into_result()
.map(|_| ())
.map_err(|e| OsError::new(e, name, arg1, arg2))
}
fn check_io_err(self) -> io::Result<()> {
2025-09-08 10:55:57 -07:00
self.into_result()
.map(|_| ())
.map_err(io::Error::from_raw_os_error)
}
}
macro_rules! impl_libc_return {
($($t:ty)*) => ($(
impl LibcReturn for $t {
type Value = Self;
#[inline(always)]
2025-09-08 10:55:57 -07:00
fn into_result(self) -> Result<Self::Value, i32> {
if self < 0 {
Err(*errno())
} else {
Ok(self)
}
}
}
)*)
}
impl_libc_return! { i8 i16 i32 i64 isize }
impl<T> LibcReturn for *mut T {
type Value = NonNull<T>;
#[inline(always)]
2025-09-08 10:55:57 -07:00
fn into_result(self) -> Result<Self::Value, i32> {
NonNull::new(self).ok_or_else(|| *errno())
}
}
#[derive(Debug)]
pub struct OsError<'a> {
code: i32,
name: &'static str,
2025-09-08 11:24:33 -07:00
arg1: Option<&'a str>,
arg2: Option<&'a str>,
}
impl OsError<'_> {
2025-09-08 10:55:57 -07:00
pub fn new<'a>(
code: i32,
name: &'static str,
arg1: Option<&'a str>,
arg2: Option<&'a str>,
) -> OsError<'a> {
OsError {
code,
name,
2025-09-08 11:24:33 -07:00
arg1,
arg2,
}
}
pub fn last_os_error<'a>(
name: &'static str,
arg1: Option<&'a str>,
arg2: Option<&'a str>,
) -> OsError<'a> {
2025-09-08 10:55:57 -07:00
Self::new(*errno(), name, arg1, arg2)
}
pub fn set_args<'a>(self, arg1: Option<&'a str>, arg2: Option<&'a str>) -> OsError<'a> {
2025-09-08 10:55:57 -07:00
Self::new(self.code, self.name, arg1, arg2)
}
fn as_io_error(&self) -> io::Error {
io::Error::from_raw_os_error(self.code)
}
}
impl Display for OsError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let error = self.as_io_error();
if self.name.is_empty() {
2025-07-02 19:36:27 -07:00
write!(f, "{error:#}")
} else {
2025-09-08 11:24:33 -07:00
match (self.arg1, self.arg2) {
(Some(arg1), Some(arg2)) => {
write!(f, "{} '{}' '{}': {:#}", self.name, arg1, arg2, error)
}
(Some(arg1), None) => {
write!(f, "{} '{}': {:#}", self.name, arg1, error)
}
_ => {
write!(f, "{}: {:#}", self.name, error)
}
}
}
}
}
impl std::error::Error for OsError<'_> {}
pub type OsResult<'a, T> = Result<T, OsError<'a>>;