1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Furi helpers.

use core::ffi::c_char;
use core::fmt::Display;
use core::time::Duration;

/// Operation status.
/// The Furi API switches between using `enum FuriStatus`, `int32_t` and `uint32_t`.
/// Since these all use the same bit representation, we can just "cast" the returns to this type.
#[repr(transparent)]
#[derive(Clone, Copy, Debug, ufmt::derive::uDebug, Eq, PartialEq)]
pub struct Status(pub i32);

impl Status {
    /// Operation completed successfully.
    pub const OK: Status = Status(0);
    /// Unspecified RTOS error: run-time error but no other error message fits.
    pub const ERR: Status = Status(-1);
    /// Operation not completed within the timeout period.
    pub const ERR_TIMEOUT: Status = Status(-2);
    /// Resource not available.
    pub const ERR_RESOURCE: Status = Status(-3);
    /// Parameter error.
    pub const ERR_PARAMETER: Status = Status(-4);
    /// System is out of memory: it was impossible to allocate or reserve memory for the operation.
    pub const ERR_NO_MEMORY: Status = Status(-5);
    /// Not allowed in ISR context: the function cannot be called from interrupt service routines.
    pub const ERR_ISR: Status = Status(-6);

    /// Describes the status result of the operation.
    pub fn description(self) -> &'static str {
        match self {
            Self::OK => "Operation completed successfully",
            Self::ERR => "Unspecified RTOS error",
            Self::ERR_TIMEOUT => "Operation not completed within the timeout period",
            Self::ERR_RESOURCE => "Resource not available",
            Self::ERR_PARAMETER => "Parameter error",
            Self::ERR_NO_MEMORY => "System is out of memory",
            Self::ERR_ISR => "Not allowed in ISR context",
            _ => "Unknown",
        }
    }

    /// Was the operation successful?
    pub fn is_ok(self) -> bool {
        self == Self::OK
    }

    /// Did the operation error?
    pub fn is_err(self) -> bool {
        self != Self::OK
    }

    /// Returns `Err(Status)` if [`Status`] is an error, otherwise `Ok(ok)`.
    pub fn err_or<T>(self, ok: T) -> Result<T, Self> {
        if self.is_err() {
            Err(self)
        } else {
            Ok(ok)
        }
    }

    /// Returns `Err(Status)` if [`Status`] is an error, otherwise `Ok(or_else(Status))`.
    pub fn err_or_else<T>(self, or_else: impl Fn(Self) -> T) -> Result<T, Self> {
        if self.is_err() {
            Err(self)
        } else {
            Ok(or_else(self))
        }
    }
}

impl Display for Status {
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
        write!(f, "{:?}: {}", self, self.description())
    }
}

impl ufmt::uDisplay for Status {
    fn fmt<W>(&self, f: &mut ufmt::Formatter<'_, W>) -> Result<(), W::Error>
    where
        W: ufmt::uWrite + ?Sized,
    {
        ufmt::uwrite!(f, "{:?}: {}", self, self.description())
    }
}

impl From<i32> for Status {
    fn from(code: i32) -> Self {
        Status(code)
    }
}

/// Low-level wrapper of a record handle.
pub struct UnsafeRecord<T> {
    name: *const c_char,
    data: *mut T,
}

impl<T> UnsafeRecord<T> {
    /// Opens a record.
    ///
    /// Safety: The caller must ensure that `record_name` lives for the
    /// duration of the object lifetime.
    ///
    /// # Safety
    ///
    /// The caller must provide a valid C-string `name`.
    pub unsafe fn open(name: *const c_char) -> Self {
        Self {
            name,
            data: crate::furi_record_open(name) as *mut T,
        }
    }

    /// Returns the record data as a raw pointer.
    pub fn as_ptr(&self) -> *mut T {
        self.data
    }
}

impl<T> Drop for UnsafeRecord<T> {
    fn drop(&mut self) {
        unsafe {
            // decrement the holders count
            crate::furi_record_close(self.name);
        }
    }
}

/// Convert [`Duration`] to ticks.
#[inline]
pub fn duration_to_ticks(duration: Duration) -> u32 {
    // This maxes out at about 50 days
    let duration_ms: u32 = duration.as_millis().try_into().unwrap_or(u32::MAX);

    unsafe { crate::furi_ms_to_ticks(duration_ms) }
}