Skip to main content

fre_rs/
validated.rs

1//! 
2//! Validated types for safe interaction with the C API and the Flash Runtime,
3//! ensuring data passed across the FFI boundary is well-formed and valid.
4//! 
5
6
7use super::*;
8
9
10pub type NonNullFREData = NonNull<c_void>;
11pub type NonNullHandle = NonNull<c_void>;
12
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15#[repr(transparent)]
16pub struct NonNegativeInt(i32);
17impl NonNegativeInt {
18    pub const MIN: Self = Self(0);
19    pub const MAX: Self = Self(i32::MAX);
20    pub fn new (value: i32) -> Option<Self> {
21        if value >= 0 {
22            Some(Self(value))
23        } else {
24            None
25        }
26    }
27    pub fn get (self) -> i32 {self.0}
28}
29impl Default for NonNegativeInt {
30    fn default() -> Self {Self::MIN}
31}
32
33
34/// A UTF-8 string stored as a NUL-terminated [`CStr`].
35/// 
36/// Use the [`ucstringify!`] macro to construct [`UCStr`] constants.
37/// 
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct UCStr(UCStrValue);
40#[derive(Debug, Clone, PartialEq, Eq)]
41enum UCStrValue {
42    Static(&'static CStr),
43    Heap(Arc<CStr>),
44}
45impl UCStr {
46    pub const fn from_literal (literal: &'static CStr) -> Result<Self, Utf8Error> {
47        match literal.to_str() {
48            Ok(_) => Ok(Self(UCStrValue::Static(literal))),
49            Err(e) => Err(e),
50        }
51    }
52    pub const unsafe fn from_literal_unchecked (literal: &'static CStr) -> Self {
53        debug_assert!(literal.to_str().is_ok());
54        Self(UCStrValue::Static(literal))
55    }
56    #[inline]
57    pub fn as_str (&self) -> &str {
58        let s = match self.0 {
59            UCStrValue::Static(s) => s.to_bytes(),
60            UCStrValue::Heap(ref s) => s.to_bytes(),
61        };
62        unsafe {str::from_utf8_unchecked(s)}
63    }
64    pub fn as_c_str (&self) -> &CStr {
65        match self.0 {
66            UCStrValue::Static(s) => s,
67            UCStrValue::Heap(ref s) => s.as_ref(),
68        }
69    }
70    /// # Borrow
71    pub fn as_ptr (&self) -> FREStr {
72        match self.0 {
73            UCStrValue::Static(s) => s.as_ptr() as FREStr,
74            UCStrValue::Heap(ref s) => s.as_ptr() as FREStr,
75        }
76    }
77    pub fn to_c_string (&self) -> CString {
78        match self.0 {
79            UCStrValue::Static(s) => s.to_owned(),
80            UCStrValue::Heap(ref s) => s.as_ref().to_owned(),
81        }
82    }
83}
84impl From<UCStr> for CString {
85    fn from(value: UCStr) -> Self {value.to_c_string()}
86}
87impl From<UCStr> for String {
88    fn from(value: UCStr) -> Self {value.to_string()}
89}
90impl TryFrom<String> for UCStr {
91    type Error = NulError;
92    fn try_from(value: String) -> Result<Self, NulError> {
93        CString::new(value).map(|s|{
94            let s = s.into();
95            Self(UCStrValue::Heap(s))
96        })
97    }
98}
99impl TryFrom<&str> for UCStr {
100    type Error = NulError;
101    fn try_from(value: &str) -> Result<Self, NulError> {
102        CString::new(value).map(|s|{
103            let s = s.into();
104            Self(UCStrValue::Heap(s))
105        })
106    }
107}
108impl TryFrom<CString> for UCStr {
109    type Error = Utf8Error;
110    fn try_from(value: CString) -> Result<Self, Utf8Error> {
111        value.to_str()?;
112        let s = value.into();
113        Ok(Self(UCStrValue::Heap(s)))
114    }
115}
116impl TryFrom<&CStr> for UCStr {
117    type Error = Utf8Error;
118    fn try_from(value: &CStr) -> Result<Self, Utf8Error> {
119        value.to_str().map(|_|{
120            let s = value.to_owned().into();
121            Self(UCStrValue::Heap(s))
122        })
123    }
124}
125impl PartialEq<str> for UCStr {
126    #[inline]
127    fn eq(&self, other: &str) -> bool { self.as_str() == other }
128}
129impl PartialEq<UCStr> for str {
130    #[inline]
131    fn eq(&self, other: &UCStr) -> bool { self == other.as_str() }
132}
133impl PartialOrd for UCStr {
134    #[inline]
135    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { self.as_str().partial_cmp(other.as_str()) }
136}
137impl Ord for UCStr {
138    #[inline]
139    fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_str().cmp(other.as_str()) }
140}
141impl std::hash::Hash for UCStr {
142    #[inline]
143    fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.as_str().hash(state) }
144}
145impl AsRef<str> for UCStr {
146    fn as_ref(&self) -> &str { self.as_str() }
147}
148impl AsRef<CStr> for UCStr {
149    fn as_ref(&self) -> &CStr { self.as_c_str() }
150}
151impl std::borrow::Borrow<str> for UCStr {
152    #[inline]
153    fn borrow(&self) -> &str { self.as_str() }
154}
155impl Display for UCStr {
156    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(self.as_str(), f) }
157}
158impl Default for UCStr {
159    fn default() -> Self {Self(UCStrValue::Static(c""))}
160}
161
162
163pub trait ToUcstrLossy {
164    fn to_ucstr_lossy(&self) -> UCStr;
165}
166impl<T: ?Sized> ToUcstrLossy for &T
167where T: ToUcstrLossy
168{fn to_ucstr_lossy(&self) -> UCStr {T::to_ucstr_lossy(self)}}
169impl<T: ?Sized> ToUcstrLossy for &mut T
170where for<'a> &'a T: ToUcstrLossy
171{fn to_ucstr_lossy(&self) -> UCStr {((*self) as &T).to_ucstr_lossy()}}
172//
173//
174impl ToUcstrLossy for UCStr {fn to_ucstr_lossy(&self) -> UCStr {self.clone()}}
175impl ToUcstrLossy for str {fn to_ucstr_lossy(&self) -> UCStr {
176    let buf = self.replace('\0', "�");
177    let buf = unsafe {CString::from_vec_unchecked(buf.into_bytes())};
178    UCStr(UCStrValue::Heap(buf.into()))
179}}
180impl ToUcstrLossy for String {fn to_ucstr_lossy(&self) -> UCStr {self.as_str().to_ucstr_lossy()}}
181impl ToUcstrLossy for CStr {fn to_ucstr_lossy(&self) -> UCStr {
182    let s = self.to_string_lossy();
183    let bytes = s.as_bytes();
184    let mut v = Vec::with_capacity(bytes.len()+1);
185    v.extend_from_slice(bytes);
186    v.push(0);
187    let s = unsafe {CString::from_vec_with_nul_unchecked(v)};
188    UCStr(UCStrValue::Heap(s.into()))
189}}
190impl ToUcstrLossy for CString {fn to_ucstr_lossy(&self) -> UCStr {self.as_c_str().to_ucstr_lossy()}}
191impl ToUcstrLossy for Object<'_> {fn to_ucstr_lossy(&self) -> UCStr {self.to_string().to_ucstr_lossy()}}
192impl ToUcstrLossy for NonNullObject<'_> {fn to_ucstr_lossy(&self) -> UCStr {self.as_object().to_ucstr_lossy()}}
193// crate::class! (...) => impl ToUcstrLossy for ...
194impl ToUcstrLossy for () {fn to_ucstr_lossy(&self) -> UCStr {crate::ucstringify!(())}}
195impl ToUcstrLossy for char {fn to_ucstr_lossy(&self) -> UCStr {
196    let buf = format!("'{}'(U+{:08X})\0", self.escape_default(),*self as u32);
197    let buf = unsafe {CString::from_vec_with_nul_unchecked(buf.into_bytes())};
198    UCStr(UCStrValue::Heap(buf.into()))
199}}
200impl ToUcstrLossy for bool {fn to_ucstr_lossy(&self) -> UCStr {
201    if *self {crate::ucstringify!(true)} else {crate::ucstringify!(false)}
202}}
203impl ToUcstrLossy for i8 {fn to_ucstr_lossy(&self) -> UCStr {
204    let buf = self.to_string().into_bytes();
205    let buf = unsafe {CString::from_vec_unchecked(buf)};
206    UCStr(UCStrValue::Heap(buf.into()))
207}}
208impl ToUcstrLossy for i16 {fn to_ucstr_lossy(&self) -> UCStr {
209    let buf = self.to_string().into_bytes();
210    let buf = unsafe {CString::from_vec_unchecked(buf)};
211    UCStr(UCStrValue::Heap(buf.into()))
212}}
213impl ToUcstrLossy for i32 {fn to_ucstr_lossy(&self) -> UCStr {
214    let buf = self.to_string().into_bytes();
215    let buf = unsafe {CString::from_vec_unchecked(buf)};
216    UCStr(UCStrValue::Heap(buf.into()))
217}}
218impl ToUcstrLossy for i64 {fn to_ucstr_lossy(&self) -> UCStr {
219    let buf = self.to_string().into_bytes();
220    let buf = unsafe {CString::from_vec_unchecked(buf)};
221    UCStr(UCStrValue::Heap(buf.into()))
222}}
223impl ToUcstrLossy for i128 {fn to_ucstr_lossy(&self) -> UCStr {
224    let buf = self.to_string().into_bytes();
225    let buf = unsafe {CString::from_vec_unchecked(buf)};
226    UCStr(UCStrValue::Heap(buf.into()))
227}}
228impl ToUcstrLossy for isize {fn to_ucstr_lossy(&self) -> UCStr {
229    let buf = self.to_string().into_bytes();
230    let buf = unsafe {CString::from_vec_unchecked(buf)};
231    UCStr(UCStrValue::Heap(buf.into()))
232}}
233impl ToUcstrLossy for u8 {fn to_ucstr_lossy(&self) -> UCStr {
234    let buf = self.to_string().into_bytes();
235    let buf = unsafe {CString::from_vec_unchecked(buf)};
236    UCStr(UCStrValue::Heap(buf.into()))
237}}
238impl ToUcstrLossy for u16 {fn to_ucstr_lossy(&self) -> UCStr {
239    let buf = self.to_string().into_bytes();
240    let buf = unsafe {CString::from_vec_unchecked(buf)};
241    UCStr(UCStrValue::Heap(buf.into()))
242}}
243impl ToUcstrLossy for u32 {fn to_ucstr_lossy(&self) -> UCStr {
244    let buf = self.to_string().into_bytes();
245    let buf = unsafe {CString::from_vec_unchecked(buf)};
246    UCStr(UCStrValue::Heap(buf.into()))
247}}
248impl ToUcstrLossy for u64 {fn to_ucstr_lossy(&self) -> UCStr {
249    let buf = self.to_string().into_bytes();
250    let buf = unsafe {CString::from_vec_unchecked(buf)};
251    UCStr(UCStrValue::Heap(buf.into()))
252}}
253impl ToUcstrLossy for u128 {fn to_ucstr_lossy(&self) -> UCStr {
254    let buf = self.to_string().into_bytes();
255    let buf = unsafe {CString::from_vec_unchecked(buf)};
256    UCStr(UCStrValue::Heap(buf.into()))
257}}
258impl ToUcstrLossy for usize {fn to_ucstr_lossy(&self) -> UCStr {
259    let buf = self.to_string().into_bytes();
260    let buf = unsafe {CString::from_vec_unchecked(buf)};
261    UCStr(UCStrValue::Heap(buf.into()))
262}}
263impl ToUcstrLossy for f32 {fn to_ucstr_lossy(&self) -> UCStr {
264    let buf = self.to_string().into_bytes();
265    let buf = unsafe {CString::from_vec_unchecked(buf)};
266    UCStr(UCStrValue::Heap(buf.into()))
267}}
268impl ToUcstrLossy for f64 {fn to_ucstr_lossy(&self) -> UCStr {
269    let buf = self.to_string().into_bytes();
270    let buf = unsafe {CString::from_vec_unchecked(buf)};
271    UCStr(UCStrValue::Heap(buf.into()))
272}}
273impl<T> ToUcstrLossy for *const T {fn to_ucstr_lossy(&self) -> UCStr {
274    let buf = format!("*const({:p})\0", self);
275    let buf = unsafe {CString::from_vec_with_nul_unchecked(buf.into_bytes())};
276    UCStr(UCStrValue::Heap(buf.into()))
277}}
278impl<T> ToUcstrLossy for *mut T {fn to_ucstr_lossy(&self) -> UCStr {
279    let buf = format!("*mut({:p})\0", self);
280    let buf = unsafe {CString::from_vec_with_nul_unchecked(buf.into_bytes())};
281    UCStr(UCStrValue::Heap(buf.into()))
282}}
283//
284//
285impl<T: ToUcstrLossy> ToUcstrLossy for Option<T> {fn to_ucstr_lossy(&self) -> UCStr {
286    if let Some(inner) = self {
287        let inner = inner.to_ucstr_lossy();
288        let mut buf = String::with_capacity(4 + 1 + inner.as_str().len() + 1 + 1);
289        buf.push_str("Some(");
290        buf.push_str(inner.as_str());
291        buf.push_str(")\0");
292        let buf = unsafe {CString::from_vec_with_nul_unchecked(buf.into_bytes())};
293        UCStr(UCStrValue::Heap(buf.into()))
294    } else {crate::ucstringify!(None)}
295}}
296impl<T: ToUcstrLossy> ToUcstrLossy for [T] {fn to_ucstr_lossy(&self) -> UCStr {
297    const SEPARATOR: &str = ", ";
298    let elems = self.iter()
299        .map(|i|i.to_ucstr_lossy())
300        .collect::<Box<[UCStr]>>();
301    let len = elems.iter()
302        .map(|s|s.as_str().len())
303        .sum::<usize>()
304        + (elems.len().saturating_sub(1))*SEPARATOR.len()
305        + 2
306        + 1;
307    let mut buf = String::with_capacity(len);
308    buf.push('[');
309    for (i, s) in elems.iter().enumerate() {
310        if i != 0 {buf.push_str(SEPARATOR);}
311        buf.push_str(s.as_str());
312    }
313    buf.push(']');
314    buf.push('\0');
315    let buf = unsafe {CString::from_vec_with_nul_unchecked(buf.into_bytes())};
316    UCStr(UCStrValue::Heap(buf.into()))
317}}
318impl<T: ToUcstrLossy, const LEN: usize> ToUcstrLossy for [T; LEN] {fn to_ucstr_lossy(&self) -> UCStr {self.as_slice().to_ucstr_lossy()}}
319impl<T: ToUcstrLossy> ToUcstrLossy for Vec<T> {fn to_ucstr_lossy(&self) -> UCStr {self.as_slice().to_ucstr_lossy()} }
320impl<T: ToUcstrLossy>
321ToUcstrLossy for (T,) {fn to_ucstr_lossy(&self) -> UCStr {
322    let s = self.0.to_ucstr_lossy();
323    let mut buf = String::with_capacity(s.as_str().len() + 3);
324    buf.push('(');
325    buf.push_str(s.as_str());
326    buf.push(')');
327    buf.push('\0');
328    let buf = unsafe {CString::from_vec_with_nul_unchecked(buf.into_bytes())};
329    UCStr(UCStrValue::Heap(buf.into()))
330}}
331macro_rules! tuple_to_ucstr {
332    ($elements:expr_2021) => {{
333        const SEPARATOR: &'static str = ", ";
334        let len = $elements.iter()
335            .map(|s|s.as_str().len())
336            .sum::<usize>()
337            + ($elements.len().saturating_sub(1))*SEPARATOR.len()
338            + 2
339            + 1;
340        let mut buf = String::with_capacity(len);
341        buf.push('(');
342        for (i, s) in $elements.iter().enumerate() {
343            if i != 0 {buf.push_str(SEPARATOR);}
344            buf.push_str(s.as_str());
345        }
346        buf.push(')');
347        buf.push('\0');
348        let buf = unsafe {CString::from_vec_with_nul_unchecked(buf.into_bytes())};
349        UCStr(UCStrValue::Heap(buf.into()))
350    }};
351}
352impl<T: ToUcstrLossy, U: ToUcstrLossy>
353ToUcstrLossy for (T, U) {fn to_ucstr_lossy(&self) -> UCStr {
354    let elements =  [self.0.to_ucstr_lossy(), self.1.to_ucstr_lossy()];
355    tuple_to_ucstr! (elements)
356}}
357impl<T: ToUcstrLossy, U: ToUcstrLossy, V: ToUcstrLossy>
358ToUcstrLossy for (T, U, V) {fn to_ucstr_lossy(&self) -> UCStr {
359    let elements = [self.0.to_ucstr_lossy(), self.1.to_ucstr_lossy(), self.2.to_ucstr_lossy()];
360    tuple_to_ucstr! (elements)
361}}
362impl<T: ToUcstrLossy, U: ToUcstrLossy, V: ToUcstrLossy, W: ToUcstrLossy>
363ToUcstrLossy for (T, U, V, W) {fn to_ucstr_lossy(&self) -> UCStr {
364    let elements = [self.0.to_ucstr_lossy(), self.1.to_ucstr_lossy(), self.2.to_ucstr_lossy(), self.3.to_ucstr_lossy()];
365    tuple_to_ucstr! (elements)
366}}
367impl<T: ToUcstrLossy, U: ToUcstrLossy, V: ToUcstrLossy, W: ToUcstrLossy, X: ToUcstrLossy>
368ToUcstrLossy for (T, U, V, W, X) {fn to_ucstr_lossy(&self) -> UCStr {
369    let elements = [self.0.to_ucstr_lossy(), self.1.to_ucstr_lossy(), self.2.to_ucstr_lossy(), self.3.to_ucstr_lossy(), self.4.to_ucstr_lossy()];
370    tuple_to_ucstr! (elements)
371}}
372impl<T: ToUcstrLossy, U: ToUcstrLossy, V: ToUcstrLossy, W: ToUcstrLossy, X: ToUcstrLossy, Y: ToUcstrLossy>
373ToUcstrLossy for (T, U, V, W, X, Y) {fn to_ucstr_lossy(&self) -> UCStr {
374    let elements = [self.0.to_ucstr_lossy(), self.1.to_ucstr_lossy(), self.2.to_ucstr_lossy(), self.3.to_ucstr_lossy(), self.4.to_ucstr_lossy(), self.5.to_ucstr_lossy()];
375    tuple_to_ucstr! (elements)
376}}
377impl<T: ToUcstrLossy, U: ToUcstrLossy, V: ToUcstrLossy, W: ToUcstrLossy, X: ToUcstrLossy, Y: ToUcstrLossy, Z: ToUcstrLossy>
378ToUcstrLossy for (T, U, V, W, X, Y, Z) {fn to_ucstr_lossy(&self) -> UCStr {
379    let elements = [self.0.to_ucstr_lossy(), self.1.to_ucstr_lossy(), self.2.to_ucstr_lossy(), self.3.to_ucstr_lossy(), self.4.to_ucstr_lossy(), self.5.to_ucstr_lossy(), self.6.to_ucstr_lossy()];
380    tuple_to_ucstr! (elements)
381}}
382impl<T: ToUcstrLossy, U: ToUcstrLossy, V: ToUcstrLossy, W: ToUcstrLossy, X: ToUcstrLossy, Y: ToUcstrLossy, Z: ToUcstrLossy, A: ToUcstrLossy>
383ToUcstrLossy for (T, U, V, W, X, Y, Z, A) {fn to_ucstr_lossy(&self) -> UCStr {
384    let elements = [self.0.to_ucstr_lossy(), self.1.to_ucstr_lossy(), self.2.to_ucstr_lossy(), self.3.to_ucstr_lossy(), self.4.to_ucstr_lossy(), self.5.to_ucstr_lossy(), self.6.to_ucstr_lossy(), self.7.to_ucstr_lossy()];
385    tuple_to_ucstr! (elements)
386}}
387