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 NonNullFREObject = 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#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct UCStr(UCStrValue);
38#[derive(Debug, Clone, PartialEq, Eq)]
39enum UCStrValue {
40    Static(&'static CStr),
41    Heap(Arc<CStr>),
42}
43impl UCStr {
44    pub const fn from_literal (literal: &'static CStr) -> Result<Self, Utf8Error> {
45        match literal.to_str() {
46            Ok(_) => Ok(Self(UCStrValue::Static(literal))),
47            Err(e) => Err(e),
48        }
49    }
50    pub const unsafe fn from_literal_unchecked (literal: &'static CStr) -> Self {
51        debug_assert!(literal.to_str().is_ok());
52        Self(UCStrValue::Static(literal))
53    }
54    #[inline]
55    pub fn as_str (&self) -> &str {
56        let s = match self.0 {
57            UCStrValue::Static(s) => s.to_bytes(),
58            UCStrValue::Heap(ref s) => s.to_bytes(),
59        };
60        unsafe {str::from_utf8_unchecked(s)}
61    }
62    pub fn as_c_str (&self) -> &CStr {
63        match self.0 {
64            UCStrValue::Static(s) => s,
65            UCStrValue::Heap(ref s) => s.as_ref(),
66        }
67    }
68    /// # Borrow
69    pub fn as_ptr (&self) -> FREStr {
70        match self.0 {
71            UCStrValue::Static(s) => s.as_ptr() as FREStr,
72            UCStrValue::Heap(ref s) => s.as_ptr() as FREStr,
73        }
74    }
75    pub fn to_c_string (&self) -> CString {
76        match self.0 {
77            UCStrValue::Static(s) => s.to_owned(),
78            UCStrValue::Heap(ref s) => s.as_ref().to_owned(),
79        }
80    }
81}
82impl From<UCStr> for CString {
83    fn from(value: UCStr) -> Self {value.to_c_string()}
84}
85impl From<UCStr> for String {
86    fn from(value: UCStr) -> Self {value.to_string()}
87}
88impl TryFrom<String> for UCStr {
89    type Error = NulError;
90    fn try_from(value: String) -> Result<Self, NulError> {
91        CString::new(value).map(|s|{
92            let s = s.into();
93            Self(UCStrValue::Heap(s))
94        })
95    }
96}
97impl TryFrom<&str> for UCStr {
98    type Error = NulError;
99    fn try_from(value: &str) -> Result<Self, NulError> {
100        CString::new(value).map(|s|{
101            let s = s.into();
102            Self(UCStrValue::Heap(s))
103        })
104    }
105}
106impl TryFrom<CString> for UCStr {
107    type Error = Utf8Error;
108    fn try_from(value: CString) -> Result<Self, Utf8Error> {
109        value.to_str()?;
110        let s = value.into();
111        Ok(Self(UCStrValue::Heap(s)))
112    }
113}
114impl TryFrom<&CStr> for UCStr {
115    type Error = Utf8Error;
116    fn try_from(value: &CStr) -> Result<Self, Utf8Error> {
117        value.to_str().map(|_|{
118            let s = value.to_owned().into();
119            Self(UCStrValue::Heap(s))
120        })
121    }
122}
123impl PartialEq<str> for UCStr {
124    #[inline]
125    fn eq(&self, other: &str) -> bool { self.as_str() == other }
126}
127impl PartialEq<UCStr> for str {
128    #[inline]
129    fn eq(&self, other: &UCStr) -> bool { self == other.as_str() }
130}
131impl PartialOrd for UCStr {
132    #[inline]
133    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { self.as_str().partial_cmp(other.as_str()) }
134}
135impl Ord for UCStr {
136    #[inline]
137    fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.as_str().cmp(other.as_str()) }
138}
139impl std::hash::Hash for UCStr {
140    #[inline]
141    fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.as_str().hash(state) }
142}
143impl AsRef<str> for UCStr {
144    fn as_ref(&self) -> &str { self.as_str() }
145}
146impl AsRef<CStr> for UCStr {
147    fn as_ref(&self) -> &CStr { self.as_c_str() }
148}
149impl std::borrow::Borrow<str> for UCStr {
150    #[inline]
151    fn borrow(&self) -> &str { self.as_str() }
152}
153impl Display for UCStr {
154    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { Display::fmt(self.as_str(), f) }
155}
156impl Default for UCStr {
157    fn default() -> Self {unsafe {Self::from_literal_unchecked(c"")}}
158}
159
160
161pub trait ToUcstrLossy {
162    fn to_ucstr_lossy(&self) -> UCStr;
163}
164impl ToUcstrLossy for UCStr {
165    fn to_ucstr_lossy(&self) -> UCStr {self.clone()}
166}
167impl<'a, O: AsObject<'a>> ToUcstrLossy for O {
168    fn to_ucstr_lossy(&self) -> UCStr {
169        self.to_string()
170            .as_str()
171            .to_ucstr_lossy()
172    }
173}
174impl<'a, O: AsObject<'a>> ToUcstrLossy for &[O] {
175    fn to_ucstr_lossy(&self) -> UCStr {
176        self.iter()
177            .map(|o|{o.to_string()})
178            .collect::<Vec<_>>()
179            .join(", ")
180            .as_str()
181            .to_ucstr_lossy()
182    }
183}
184impl ToUcstrLossy for &str {
185    fn to_ucstr_lossy(&self) -> UCStr {
186        self.replace('\0', "�")
187            .try_into()
188            .unwrap()
189    }
190}
191impl ToUcstrLossy for String {
192    fn to_ucstr_lossy(&self) -> UCStr {
193        self.as_str()
194            .to_ucstr_lossy()
195    }
196}
197