file_metadata/mditem/
mod.rs

1extern crate file_metadata_mditem_macros;
2
3use std::marker::PhantomData;
4
5use core_foundation_sys::base::{CFAllocatorRef, kCFAllocatorDefault};
6use core_foundation_sys::base::{TCFTypeRef, CFTypeID};
7use core_foundation::url::{CFURL, CFURLRef};
8use core_foundation::string::{CFString, CFStringRef};
9use core_foundation::array::{CFArray, CFArrayRef};
10use core_foundation::base::TCFType;
11use libc::c_void;
12
13#[repr(C)]
14pub struct __MDItemRef(c_void);
15pub type MDItemRef = *const __MDItemRef;
16
17#[link(name = "CoreServices", kind = "framework")]
18extern "C" {
19    fn MDItemCreate(allocator: CFAllocatorRef, path: CFStringRef) -> MDItemRef;
20    fn MDItemCreateWithURL(allocator: CFAllocatorRef, url: CFURLRef) -> MDItemRef;
21    fn MDItemCopyAttribute(item: MDItemRef, name: CFStringRef) -> *const c_void;
22    fn MDItemCopyAttributeNames(item: MDItemRef) -> CFArrayRef;
23    fn MDItemGetTypeID() -> CFTypeID;
24}
25
26pub struct Attribute<T> {
27    pub key_getter: fn() -> CFStringRef,
28    pub phantom: PhantomData<T>
29}
30
31impl<T: TCFType> Attribute<T> {
32    pub fn key(&self) -> CFStringRef {
33        (self.key_getter)()
34    }
35}
36
37pub mod attributes;
38
39declare_TCFType!{
40    MDItem, MDItemRef
41}
42impl_TCFType!(MDItem, MDItemRef, MDItemGetTypeID);
43
44impl MDItem {
45    pub fn from_string(path: CFString) -> Option<MDItem> {
46        let ptr = unsafe { MDItemCreate(kCFAllocatorDefault, path.as_concrete_TypeRef()) };
47
48        if ptr.is_null() {
49            None
50        } else {
51            Some(unsafe { TCFType::wrap_under_create_rule(ptr) })
52        }
53    }
54
55    pub fn from_url(url: CFURL) -> Option<MDItem> {
56        let ptr = unsafe { MDItemCreateWithURL(kCFAllocatorDefault, url.as_concrete_TypeRef()) };
57
58        if ptr.is_null() {
59            None
60        } else {
61            Some(unsafe { TCFType::wrap_under_create_rule(ptr) })
62        }
63    }
64
65    pub fn attributes(&self) -> CFArray<CFString> {
66        unsafe {
67            TCFType::wrap_under_create_rule(MDItemCopyAttributeNames(self.0))
68        }
69    }
70
71    #[inline]
72    pub fn get<T>(&self, attr: Attribute<T>) -> Option<T> where T: TCFType {
73        let value = unsafe { MDItemCopyAttribute(self.0, attr.key()) };
74
75        if value.is_null() {
76            None
77        } else {
78            Some(unsafe { T::wrap_under_create_rule(T::Ref::from_void_ptr(value)) })
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::{MDItem, attributes::{CFBundleIdentifier, DisplayName}};
86    use core_foundation::url::CFURL;
87    use core_foundation::string::CFString;
88    use std::path::Path;
89
90    #[test]
91    fn get_safari_bundle_id_and_display_name() {
92        let safari_url = CFURL::from_path(Path::new("/Applications/Safari.app"), true).unwrap();
93        let safari_item = MDItem::from_url(safari_url).unwrap();
94        assert_eq!(safari_item.get(CFBundleIdentifier), Some(CFString::new("com.apple.Safari")));
95        assert_eq!(safari_item.get(DisplayName), Some(CFString::new("Safari")));
96    }
97}