Skip to content

Commit

Permalink
Merge pull request #107 from madsmtm/objc2
Browse files Browse the repository at this point in the history
macOS: Use `objc2-foundation`
  • Loading branch information
Byron authored May 15, 2024
2 parents c14d904 + 58b99ef commit 46585ce
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 104 deletions.
12 changes: 10 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,18 @@ tempfile = "3.8.0"


[target.'cfg(target_os = "macos")'.dependencies]
objc = "0.2.7"
objc2 = "0.5.1"
objc2-foundation = { version = "0.2.0", features = [
"NSError",
"NSFileManager",
"NSString",
"NSURL",
] }

[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "ios"), not(target_os = "android")))'.dependencies]
chrono = { version = "0.4.31", optional = true, default-features = false, features = ["clock"] }
chrono = { version = "0.4.31", optional = true, default-features = false, features = [
"clock",
] }
libc = "0.2.149"
scopeguard = "1.2.0"
url = "2.4.1"
Expand Down
113 changes: 11 additions & 102 deletions src/macos.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,10 @@
use std::{ffi::OsString, path::PathBuf, process::Command};

use log::{trace, warn};
use objc::{
class, msg_send,
runtime::{Object, BOOL, NO},
sel, sel_impl,
};
use log::trace;
use objc2_foundation::{NSFileManager, NSString, NSURL};

use crate::{into_unknown, Error, TrashContext};

#[link(name = "Foundation", kind = "framework")]
extern "C" {
// Using an empty scope to just link against the foundation framework,
// to find the NSFileManager, but we don't need anything else from it.
}

#[allow(non_camel_case_types)]
type id = *mut Object;
#[allow(non_upper_case_globals)]
const nil: id = std::ptr::null_mut();
#[allow(non_upper_case_globals)]
const NSUTF8StringEncoding: usize = 4;

#[derive(Copy, Clone, Debug)]
pub enum DeleteMethod {
/// Use an `osascript`, asking the Finder application to delete the files.
Expand Down Expand Up @@ -89,49 +72,21 @@ impl TrashContext {

fn delete_using_file_mgr(full_paths: Vec<String>) -> Result<(), Error> {
trace!("Starting delete_using_file_mgr");
let url_cls = class!(NSURL);
let file_mgr_cls = class!(NSFileManager);
let file_mgr: id = unsafe { msg_send![file_mgr_cls, defaultManager] };
let file_mgr = unsafe { NSFileManager::defaultManager() };
for path in full_paths {
let string = to_ns_string(&path);
let string = NSString::from_str(&path);

trace!("Starting fileURLWithPath");
let url: id = unsafe { msg_send![url_cls, fileURLWithPath:string.ptr] };
if url == nil {
return Err(Error::Unknown {
description: format!("Failed to convert a path to an NSURL. Path: '{path}'"),
});
}
let url = unsafe { NSURL::fileURLWithPath(&string) };
trace!("Finished fileURLWithPath");
// WARNING: I don't know why but if we try to call release on the url, it sometimes
// crashes with SIGSEGV, so we instead don't try to release the url
// let url = OwnedObject { ptr: url };
let mut error: id = nil;

trace!("Calling trashItemAtURL");
let success: BOOL = unsafe {
msg_send![
file_mgr,
trashItemAtURL:url
resultingItemURL:nil
error:(&mut error as *mut id)
]
};
let res = unsafe { file_mgr.trashItemAtURL_resultingItemURL_error(&url, None) };
trace!("Finished trashItemAtURL");
if success == NO {
trace!("success was NO");
if error == nil {
return Err(Error::Unknown {
description: format!(
"While deleting '{path}', `trashItemAtURL` returned with failure but no error was specified.",
),
});
}
let code: isize = unsafe { msg_send![error, code] };
let domain: id = unsafe { msg_send![error, domain] };
let domain = unsafe { ns_string_to_rust(domain)? };

if let Err(err) = res {
return Err(Error::Unknown {
description: format!(
"While deleting '{path}', `trashItemAtURL` failed, code: {code}, domain: {domain}",
),
description: format!("While deleting '{path}', `trashItemAtURL` failed: {err}"),
});
}
}
Expand Down Expand Up @@ -180,52 +135,6 @@ fn to_string<T: Into<OsString>>(str_in: T) -> Result<String, Error> {
}
}

/// Uses the Drop trait to `release` the object held by `ptr`.
#[repr(transparent)]
struct OwnedObject {
pub ptr: id,
}
impl Drop for OwnedObject {
fn drop(&mut self) {
#[allow(clippy::let_unit_value)]
{
let () = unsafe { msg_send![self.ptr, release] };
}
}
}

fn to_ns_string(s: &str) -> OwnedObject {
trace!("Called to_ns_string on '{}'", s);
let utf8 = s.as_bytes();
let string_cls = class!(NSString);
let alloced_string: id = unsafe { msg_send![string_cls, alloc] };
let mut string: id = unsafe {
msg_send![
alloced_string,
initWithBytes:utf8.as_ptr()
length:utf8.len()
encoding:NSUTF8StringEncoding
]
};
if string == nil {
warn!("initWithBytes returned nil when trying to convert a rust string to an NSString");
string = unsafe { msg_send![alloced_string, init] };
}
OwnedObject { ptr: string }
}

/// Safety: `string` is assumed to be a pointer to an NSString
unsafe fn ns_string_to_rust(string: id) -> Result<String, Error> {
if string == nil {
return Ok(String::new());
}
let utf8_bytes: *const u8 = msg_send![string, UTF8String];
let utf8_len: usize = msg_send![string, lengthOfBytesUsingEncoding: NSUTF8StringEncoding];
let str_slice = std::slice::from_raw_parts(utf8_bytes, utf8_len);
let rust_str = std::str::from_utf8(str_slice).map_err(into_unknown)?;
Ok(rust_str.to_owned())
}

#[cfg(test)]
mod tests {
use crate::{
Expand Down

0 comments on commit 46585ce

Please sign in to comment.