#![allow(dead_code)]
use libc::{strlen, PATH_MAX};
use objc::class;
use objc::msg_send;
use objc::runtime::Object;
use objc::sel;
use objc::sel_impl;
use objc_foundation::{INSArray, INSString, NSArray, NSObject, NSString};
use std::ffi::OsStr;
use std::os::raw::{c_char, c_int, c_ulong};
use std::os::unix::ffi::OsStrExt;
use std::path::PathBuf;
pub const NS_LIBRARY_DIRECTORY: c_ulong = 5;
pub const NS_USER_DIRECTORY: c_ulong = 7;
pub const NS_DOCUMENT_DIRECTORY: c_ulong = 9;
pub const NS_CACHES_DIRECTORY: c_ulong = 13;
pub const NS_APPLICATION_SUPPORT_DIRECTORY: c_ulong = 14;
pub const NS_DOWNLOADS_DIRECTORY: c_ulong = 15;
const NS_USER_DOMAIN_MASK: c_ulong = 1;
pub fn get_macos_dir(directory: c_ulong) -> Option<String> {
unsafe {
let nsfilemanager = class!(NSFileManager);
let instance: *mut Object = msg_send![nsfilemanager, defaultManager];
let directories: *const NSArray<NSObject> =
msg_send![instance, URLsForDirectory:directory inDomains:NS_USER_DOMAIN_MASK];
if let Some(obj) = (*directories).first_object() {
let str: *const NSString = msg_send![obj, path];
if str.is_null() {
return None;
}
let data = (*str).as_str();
let copy = String::from(data);
Some(copy)
} else {
None
}
}
}
pub fn get_macos_dir_fail_if_sandbox(directory: c_ulong) -> Option<PathBuf> {
if let Some(dir) = get_macos_dir(directory) {
if dir.contains("Library/Containers/") {
None
} else {
Some(PathBuf::from(dir))
}
} else {
None
}
}
extern "C" {
pub fn _NSGetExecutablePath(buf: *mut c_char, bufsize: *mut u32) -> c_int;
}
pub fn get_exe_path() -> Option<PathBuf> {
let mut buf: [c_char; PATH_MAX as usize] = [0; PATH_MAX as usize];
let mut size: u32 = PATH_MAX as u32;
unsafe {
let res = _NSGetExecutablePath(&mut buf as _, &mut size as _);
if res == -1 {
let mut v = Vec::with_capacity(size as usize);
let res = _NSGetExecutablePath(v.as_mut_ptr(), &mut size as _);
if res != 0 {
return None;
}
let str = OsStr::from_bytes(std::mem::transmute(&v[..size as usize]));
return Some(PathBuf::from(str));
}
if res != 0 {
return None;
}
let len = strlen(buf.as_ptr());
let str = OsStr::from_bytes(std::mem::transmute(&buf[..len as usize]));
Some(PathBuf::from(str))
}
}
pub fn get_bundled_asset(name: &str) -> Option<PathBuf> {
let (file_path, file_name) = name
.rfind('/')
.map(|v| (Some(&name[..v]), &name[v + 1..]))
.unwrap_or_else(|| (None, name));
let (res_name, res_ext) = file_name
.rfind('.')
.map(|v| (&file_name[..v], &file_name[v + 1..]))
.unwrap_or_else(|| (file_name, ""));
unsafe {
const NS_UTF8_STRING_ENCODING: c_ulong = 4;
let nsstring = class!(NSString);
let nsbundle = class!(NSBundle);
let bundle: *mut Object = msg_send![nsbundle, mainBundle];
if bundle.is_null() {
return None;
}
let mut ns_res_name: *mut Object = msg_send![nsstring, alloc];
let mut ns_res_ext: *mut Object = msg_send![nsstring, alloc];
ns_res_name = msg_send![ns_res_name,
initWithBytes: res_name.as_bytes().as_ptr()
length: res_name.len() as c_ulong
encoding: NS_UTF8_STRING_ENCODING
];
ns_res_ext = msg_send![ns_res_ext,
initWithBytes: res_ext.as_bytes().as_ptr()
length: res_ext.len() as c_ulong
encoding: NS_UTF8_STRING_ENCODING
];
let str: *const NSString = match file_path {
None => msg_send![bundle, pathForResource: ns_res_name ofType: ns_res_ext],
Some(subpath) => {
let mut ns_subpath: *mut Object = msg_send![nsstring, alloc];
ns_subpath = msg_send![ns_subpath,
initWithBytes: subpath.as_bytes().as_ptr()
length: subpath.len() as c_ulong
encoding: NS_UTF8_STRING_ENCODING
];
let str = msg_send![bundle, pathForResource: ns_res_name ofType: ns_res_ext inDirectory: ns_subpath];
let _: () = msg_send![ns_subpath, release]; str
}
};
let _: () = msg_send![ns_res_ext, release]; let _: () = msg_send![ns_res_name, release]; if str.is_null() {
return None;
}
let data = (*str).as_str();
Some(PathBuf::from(data))
}
}