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}
166macro_rules! impl_to_ucstr_lossy {
167    {// #0
168        ref $self:ident: $ty:ty
169        $body:block
170        $(<$($gps:tt)+)? 
171    } => {
172        impl $(<$($gps)+)? ToUcstrLossy for & $ty {fn to_ucstr_lossy(&self) -> UCStr {<$ty as ToUcstrLossy>::to_ucstr_lossy(*self)}}
173        impl $(<$($gps)+)? ToUcstrLossy for &mut $ty {fn to_ucstr_lossy(&self) -> UCStr {<$ty as ToUcstrLossy>::to_ucstr_lossy(*self)}}
174        impl $(<$($gps)+)? ToUcstrLossy for $ty {fn to_ucstr_lossy(&self) -> UCStr {let $self = self; $body}}
175    };
176}
177impl_to_ucstr_lossy! {ref this: UCStr {
178    this.clone()
179}}
180impl_to_ucstr_lossy! {ref this: str {
181    this.replace('\0', "�")
182        .try_into()
183        .unwrap()
184}}
185impl_to_ucstr_lossy! {ref this: String {
186    this.as_str().to_ucstr_lossy()
187}}
188impl_to_ucstr_lossy! {ref this: CStr {
189    let s = this.to_string_lossy();
190    let bytes = s.as_bytes();
191    let mut v = Vec::with_capacity(bytes.len()+1);
192    v.extend_from_slice(bytes);
193    v.push(0);
194    let s = unsafe {CString::from_vec_unchecked(v)};
195    UCStr(UCStrValue::Heap(s.into()))
196}}
197impl_to_ucstr_lossy! {ref this: CString {
198    this.as_c_str().to_ucstr_lossy()
199}}
200impl<'a, O: AsObject<'a>> ToUcstrLossy for O {
201    fn to_ucstr_lossy(&self) -> UCStr {
202        self.to_string()
203            .as_str()
204            .to_ucstr_lossy()
205    }
206}
207impl<'a> ToUcstrLossy for & as3::Object<'a> {fn to_ucstr_lossy(&self) -> UCStr {<as3::Object as ToUcstrLossy>::to_ucstr_lossy(*self)}}
208impl<'a> ToUcstrLossy for &mut as3::Object<'a> {fn to_ucstr_lossy(&self) -> UCStr {<as3::Object as ToUcstrLossy>::to_ucstr_lossy(*self)}}
209// crate::class! (...);
210impl_to_ucstr_lossy! {ref this: Option<O> {
211    if let Some(object) = this {
212        object.to_ucstr_lossy()
213    } else {crate::ucstringify!(null)}
214} <'a, O: AsObject<'a>> }
215impl_to_ucstr_lossy! {ref this: [O] {
216    this.iter()
217        .map(|object|object.to_string())
218        .collect::<Vec<String>>()
219        .join(", ")
220        .as_str()
221        .to_ucstr_lossy()
222} <'a, O: AsObject<'a>> }
223impl_to_ucstr_lossy! {ref this: [Option<O>] {
224    this.iter()
225        .map(|object|{
226            if let Some(object) = object {
227                object.to_string()
228            } else {String::from("null")}
229        })
230        .collect::<Vec<String>>()
231        .join(", ")
232        .as_str()
233        .to_ucstr_lossy()
234} <'a, O: AsObject<'a>> }
235impl_to_ucstr_lossy! {ref this: [O; LEN] {
236    this.as_slice().to_ucstr_lossy()
237} <'a, O: AsObject<'a>,
238const LEN: usize> }
239impl_to_ucstr_lossy! {ref this: [Option<O>; LEN] {
240    this.as_slice().to_ucstr_lossy()
241} <'a, O: AsObject<'a>,
242const LEN: usize> }
243impl_to_ucstr_lossy! {ref this: Vec<O> {
244    this.as_slice().to_ucstr_lossy()
245} <'a, O: AsObject<'a>> }
246impl_to_ucstr_lossy! {ref this: Vec<Option<O>> {
247    this.as_slice().to_ucstr_lossy()
248} <'a, O: AsObject<'a>> }
249