use alloc::{string::String, vec::Vec};
use core::ffi::c_void;
type CFIndex = isize;
type Boolean = u8;
type CFStringEncoding = u32;
#[allow(non_upper_case_globals)]
const kCFStringEncodingUTF8: CFStringEncoding = 0x08000100;
#[repr(C)]
#[derive(Clone, Copy)]
struct CFRange {
pub location: CFIndex,
pub length: CFIndex,
}
type CFTypeRef = *const c_void;
#[repr(C)]
struct __CFArray(c_void);
type CFArrayRef = *const __CFArray;
#[repr(C)]
struct __CFString(c_void);
type CFStringRef = *const __CFString;
#[link(name = "CoreFoundation", kind = "framework")]
extern "C" {
fn CFArrayGetCount(theArray: CFArrayRef) -> CFIndex;
fn CFArrayGetValueAtIndex(theArray: CFArrayRef, idx: CFIndex) -> *const c_void;
fn CFStringGetLength(theString: CFStringRef) -> CFIndex;
fn CFStringGetBytes(
theString: CFStringRef,
range: CFRange,
encoding: CFStringEncoding,
lossByte: u8,
isExternalRepresentation: Boolean,
buffer: *mut u8,
maxBufLen: CFIndex,
usedBufLen: *mut CFIndex,
) -> CFIndex;
fn CFRelease(cf: CFTypeRef);
fn CFLocaleCopyPreferredLanguages() -> CFArrayRef;
}
pub(crate) fn get() -> impl Iterator<Item = String> {
let preferred_langs = get_languages();
let mut idx = 0;
#[allow(clippy::as_conversions)]
core::iter::from_fn(move || unsafe {
let (langs, num_langs) = preferred_langs.as_ref()?;
if idx >= *num_langs {
return None;
}
let locale = CFArrayGetValueAtIndex(langs.0, idx) as CFStringRef;
idx += 1;
let str_len = CFStringGetLength(locale);
let range = CFRange {
location: 0,
length: str_len,
};
let mut capacity = 0;
CFStringGetBytes(
locale,
range,
kCFStringEncodingUTF8,
0,
false as Boolean,
core::ptr::null_mut(),
0,
&mut capacity,
);
if capacity == 0 {
return None;
}
let mut buffer = Vec::with_capacity(capacity as usize);
let mut out_len = 0;
CFStringGetBytes(
locale,
range,
kCFStringEncodingUTF8,
0,
false as Boolean,
buffer.as_mut_ptr(),
capacity as CFIndex,
&mut out_len,
);
assert!(out_len <= capacity);
buffer.set_len(out_len as usize);
String::from_utf8(buffer).ok()
})
}
fn get_languages() -> Option<(CFArray, CFIndex)> {
unsafe {
let langs = CFLocaleCopyPreferredLanguages();
if !langs.is_null() {
let langs = CFArray(langs);
let count = CFArrayGetCount(langs.0);
if count != 0 {
Some((langs, count))
} else {
None
}
} else {
None
}
}
}
struct CFArray(CFArrayRef);
impl Drop for CFArray {
fn drop(&mut self) {
unsafe { CFRelease(self.0.cast()) }
}
}