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