macro_rules! ns_string_wrapper {
(
$(#[$meta:meta])+
$vis:vis wrapper $wrapper:ident;
) => {
objc_object_wrapper! {
$(#[$meta])+
$vis wrapper $wrapper: $crate::foundation::NSString<'static>;
}
impl std::fmt::Debug for $wrapper {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for $wrapper {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
};
(
$(#[$meta:meta])+
$vis:vis wrapper $wrapper:ident <$lifetime:lifetime>;
) => {
objc_object_wrapper! {
$(#[$meta])+
$vis wrapper $wrapper<$lifetime>: $crate::foundation::NSString<$lifetime>;
}
impl std::fmt::Debug for $wrapper<'_> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Display for $wrapper<'_> {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
};
}
#[macro_export]
macro_rules! ns_string {
($s:expr) => {{
extern "C" {
static __CFConstantStringClassReference: $crate::_priv::c_void;
}
let cfstring_ptr: *const $crate::_priv::c_void = {
const INPUT: &[u8] = $crate::_priv::cfstring::trim_trailing_nul($s);
if $crate::_priv::cfstring::is_ascii(INPUT) {
#[repr(C)]
struct Ascii {
data: [u8; INPUT.len()],
nul: u8,
}
const ASCII: Ascii = Ascii {
data: unsafe { *$crate::_priv::std::mem::transmute::<_, &_>(INPUT.as_ptr()) },
nul: 0,
};
const ASCII_ARRAY: &[u8; INPUT.len() + 1] =
unsafe { $crate::_priv::std::mem::transmute(&ASCII) };
#[link_section = "__DATA,__cfstring,regular"]
static CFSTRING: $crate::_priv::cfstring::CFStringAscii =
$crate::_priv::cfstring::CFStringAscii::new(
unsafe { &__CFConstantStringClassReference },
ASCII_ARRAY.as_ptr(),
INPUT.len(),
);
CFSTRING.as_ptr()
} else {
const UTF16_FULL: (&[u16; INPUT.len()], usize) = {
let mut out = [0u16; INPUT.len()];
let mut iter = $crate::_priv::cfstring::utf16::EncodeUtf16Iter::new(INPUT);
let mut written = 0;
while let Some((state, chars)) = iter.next() {
iter = state;
out[written] = chars.repr[0];
written += 1;
if chars.len > 1 {
out[written] = chars.repr[1];
written += 1;
}
}
(&{ out }, written)
};
#[repr(C)]
struct Utf16 {
data: [u16; UTF16_FULL.1],
nul: u16,
}
const UTF16: Utf16 = Utf16 {
data: unsafe {
*$crate::_priv::std::mem::transmute::<_, &_>(UTF16_FULL.0.as_ptr())
},
nul: 0,
};
const UTF16_ARRAY: &[u16; UTF16_FULL.1 + 1] =
unsafe { $crate::_priv::std::mem::transmute(&UTF16) };
#[link_section = "__DATA,__cfstring,regular"]
static CFSTRING: $crate::_priv::cfstring::CFStringUtf16 =
$crate::_priv::cfstring::CFStringUtf16::new(
unsafe { &__CFConstantStringClassReference },
UTF16_ARRAY.as_ptr(),
UTF16_FULL.1,
);
CFSTRING.as_ptr()
}
};
union Cast<T: 'static> {
pointer: *const T,
reference: &'static T,
}
#[allow(unused_unsafe)]
let ns_string: &$crate::foundation::NSString = unsafe {
Cast {
pointer: cfstring_ptr.cast(),
}
.reference
};
ns_string
}};
}
#[cfg(test)]
mod tests {
use super::super::NSString;
#[test]
fn ns_string() {
macro_rules! test {
($($s:expr,)+) => {$({
static STRING: &NSString = ns_string!($s);
assert_eq!(STRING.to_string(), $s);
})+};
}
test! {
"asdf",
"🦀",
"🏳️🌈",
"𝄞music",
"abcd【e】fg",
"abcd⒠fg",
"ääääh",
"lööps, bröther?",
"\u{fffd} \u{fffd} \u{fffd}",
"讓每個人都能打造出。",
}
}
}