2020-03-05 13:33:11 +11:00
|
|
|
//! A wrapper for `NSFileManager`, which is necessary for macOS/iOS (the sandbox makes things
|
|
|
|
//! tricky, and this transparently handles it for you).
|
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
use std::error::Error;
|
|
|
|
use std::sync::RwLock;
|
2020-03-05 13:33:11 +11:00
|
|
|
|
|
|
|
use objc_id::Id;
|
2020-03-11 14:09:24 +11:00
|
|
|
use objc::runtime::{BOOL, Object};
|
2020-03-05 13:33:11 +11:00
|
|
|
use objc::{class, msg_send, sel, sel_impl};
|
2020-03-11 14:09:24 +11:00
|
|
|
use url::Url;
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-18 12:19:56 +11:00
|
|
|
use crate::foundation::{id, nil, NO, NSString, NSUInteger};
|
2020-03-11 14:09:24 +11:00
|
|
|
use crate::error::AppKitError;
|
2020-03-05 13:33:11 +11:00
|
|
|
use crate::filesystem::enums::{SearchPathDirectory, SearchPathDomainMask};
|
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
pub struct FileManager {
|
|
|
|
pub manager: RwLock<Id<Object>>
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
impl Default for FileManager {
|
|
|
|
/// Returns a default file manager, which maps to the default system file manager. For common
|
|
|
|
/// and simple tasks, with no callbacks, you might want this.
|
2020-03-05 13:33:11 +11:00
|
|
|
fn default() -> Self {
|
2020-03-11 14:09:24 +11:00
|
|
|
FileManager {
|
|
|
|
manager: RwLock::new(unsafe {
|
2020-03-05 13:33:11 +11:00
|
|
|
let manager: id = msg_send![class!(NSFileManager), defaultManager];
|
|
|
|
Id::from_ptr(manager)
|
2020-03-11 14:09:24 +11:00
|
|
|
})
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
impl FileManager {
|
|
|
|
/// Returns a new FileManager that opts in to delegate methods.
|
|
|
|
pub fn new() -> Self {
|
|
|
|
FileManager {
|
|
|
|
manager: RwLock::new(unsafe {
|
|
|
|
let manager: id = msg_send![class!(NSFileManager), new];
|
|
|
|
Id::from_ptr(manager)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Given a directory/domain combination, will attempt to get the directory that matches.
|
|
|
|
/// Returns a PathBuf that wraps the given location. If there's an error on the Objective-C
|
|
|
|
/// side, we attempt to catch it and bubble it up.
|
|
|
|
pub fn get_directory(&self, directory: SearchPathDirectory, in_domain: SearchPathDomainMask) -> Result<Url, Box<dyn Error>> {
|
2020-03-05 13:33:11 +11:00
|
|
|
let dir: NSUInteger = directory.into();
|
|
|
|
let mask: NSUInteger = in_domain.into();
|
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
let directory = unsafe {
|
|
|
|
let manager = self.manager.read().unwrap();
|
|
|
|
let dir: id = msg_send![&**manager, URLForDirectory:dir
|
2020-03-05 13:33:11 +11:00
|
|
|
inDomain:mask
|
|
|
|
appropriateForURL:nil
|
|
|
|
create:NO
|
|
|
|
error:nil];
|
|
|
|
|
2020-03-18 12:19:56 +11:00
|
|
|
NSString::wrap(msg_send![dir, absoluteString]).to_str()
|
2020-03-11 14:09:24 +11:00
|
|
|
};
|
|
|
|
|
|
|
|
Url::parse(directory).map_err(|e| e.into())
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
/// Given two paths, moves file (`from`) to the location specified in `to`. This can result in
|
|
|
|
/// an error on the Objective-C side, which we attempt to handle and bubble up as a result if
|
|
|
|
/// so.
|
|
|
|
pub fn move_item(&self, from: Url, to: Url) -> Result<(), Box<dyn Error>> {
|
2020-03-18 12:19:56 +11:00
|
|
|
let from = NSString::new(from.as_str());
|
|
|
|
let to = NSString::new(to.as_str());
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-18 12:19:56 +11:00
|
|
|
unsafe {
|
|
|
|
let from_url: id = msg_send![class!(NSURL), URLWithString:from.into_inner()];
|
|
|
|
let to_url: id = msg_send![class!(NSURL), URLWithString:to.into_inner()];
|
2020-03-11 14:09:24 +11:00
|
|
|
|
|
|
|
// This should potentially be write(), but the backing class handles this logic
|
|
|
|
// already, so... going to leave it as read.
|
|
|
|
let manager = self.manager.read().unwrap();
|
|
|
|
|
|
|
|
let error: id = nil;
|
|
|
|
let result: BOOL = msg_send![&**manager, moveItemAtURL:from_url toURL:to_url error:&error];
|
|
|
|
if result == NO {
|
2020-03-16 17:10:43 +11:00
|
|
|
return Err(AppKitError::new(error).into());
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
2020-03-11 14:09:24 +11:00
|
|
|
}
|
2020-03-05 13:33:11 +11:00
|
|
|
|
2020-03-11 14:09:24 +11:00
|
|
|
Ok(())
|
2020-03-05 13:33:11 +11:00
|
|
|
}
|
|
|
|
}
|