file_metadata/
mdquery.rs

1#![allow(non_upper_case_globals)]
2
3use core_foundation_sys::base::{CFTypeID, CFIndex, CFAllocatorRef, kCFAllocatorDefault};
4use core_foundation::base::{TCFType, FromVoid, ItemRef};
5use core_foundation::string::{CFString, CFStringRef};
6use core_foundation::array::{CFArray, CFArrayRef};
7use libc::{c_void, c_ulong};
8
9use crate::mditem::MDItem;
10
11pub type OptionBits = u32;
12pub type CFOptionFlags = c_ulong;
13
14#[repr(C)]
15pub struct __MDQuery(c_void);
16pub type MDQueryRef = *const __MDQuery;
17
18bitflags! {
19    pub struct MDQueryOptionFlags: CFOptionFlags {
20        const SYNC = 1;
21        const WANTS_UPDATES = 4;
22        const ALLOW_FS_TRANSLATION = 8;
23    }
24}
25
26#[repr(C)]
27#[derive(Debug)]
28pub struct MDQueryBatchingParams {
29    first_max_num: usize,
30    first_max_ms: usize,
31    progress_max_num: usize,
32    progress_max_ms: usize,
33    update_max_num: usize,
34    update_max_ms: usize,
35}
36
37// use core_foundation::base::{CFComparisonResult, CFTypeRef};
38
39// type MDQueryCreateValueFunction = extern "C" fn(query: MDQueryRef, attrName: CFStringRef, attrValue: CFTypeRef, context: *const c_void) -> *const c_void;
40// type MDQueryCreateResultFunction = extern "C" fn(query: MDQueryRef, item: MDItemRef, context: *const c_void) -> *const c_void;
41// type MDQuerySortComparatorFunction = extern "C" fn(attrs1: *const CFTypeRef, attrs2: *const CFTypeRef, context: *const c_void) -> CFComparisonResult;
42
43#[allow(non_snake_case)]
44#[link(name = "CoreServices", kind = "framework")]
45extern "C" {
46    fn MDQueryCreate(allocator: CFAllocatorRef, queryString: CFStringRef, valueListAttrs: CFArrayRef, sortingAttrs: CFArrayRef) -> MDQueryRef;
47    // fn MDQueryCreateSubset(allocator: CFAllocatorRef, query: MDQueryRef, queryString: CFStringRef, valueListAttrs: CFArrayRef, sortingAttrs: CFArrayRef) -> MDQueryRef;
48    // fn MDQuerySetSearchScope(query: MDQueryRef, scopeDirectories: CFArrayRef, scopeOptions: OptionBits);
49    // fn MDQuerySetDispatchQueue(query: MDQueryRef, queue: *const c_void/* TODO */);
50
51    // fn MDQuerySetMaxCount(query: MDQueryRef, size: CFIndex);
52    // fn MDQueryGetBatchingParameters(query: MDQueryRef) -> MDQueryBatchingParams;
53    // fn MDQuerySetBatchingParameters(query: MDQueryRef, params: MDQueryBatchingParams);
54    // fn MDQueryCopyValueListAttributes(query: MDQueryRef) -> CFArrayRef;
55    // fn MDQueryCopySortingAttributes(query: MDQueryRef) -> CFArrayRef;
56    fn MDQueryCopyQueryString(query: MDQueryRef) -> CFStringRef;
57
58
59    fn MDQueryExecute(query: MDQueryRef, optionFlags: CFOptionFlags) -> bool;
60    fn MDQueryStop(query: MDQueryRef);
61    // fn MDQueryDisableUpdates(query: MDQueryRef);
62    // fn MDQueryEnableUpdates(query: MDQueryRef);
63    // fn MDQueryIsGatheringComplete(query: MDQueryRef) -> bool;
64
65    // fn MDQueryCopyValuesOfAttribute(query: MDQueryRef, name: CFStringRef) -> CFArrayRef;
66    // fn MDQueryGetAttributeValueOfResultAtIndex(query: MDQueryRef, name: CFStringRef, idx: CFIndex) -> *const c_void;
67    // fn MDQueryGetCountOfResultsWithAttributeValue(query: MDQueryRef, name: CFStringRef, value: CFTypeRef) -> CFIndex;
68    // fn MDQueryGetIndexOfResult(query: MDQueryRef, result: *const c_void) -> CFIndex;
69    fn MDQueryGetResultAtIndex(query: MDQueryRef, idx: CFIndex) -> *const c_void;
70    fn MDQueryGetResultCount(query: MDQueryRef) -> CFIndex;
71    // fn MDQuerySetSortComparatorBlock(query: MDQueryRef, comparator: extern "C" fn(attrs1: *const CFTypeRef, attrs2: *const CFTypeRef) -> CFComparisonResult);
72
73    fn MDQueryGetTypeID() -> CFTypeID;
74}
75
76pub struct MDQueryIterator<'a> {
77    query: &'a MDQuery,
78    index: CFIndex,
79    len: CFIndex,
80}
81
82impl<'a> Iterator for MDQueryIterator<'a> {
83    type Item = ItemRef<'a, MDItem>;
84
85    fn next(&mut self) -> Option<Self::Item> {
86        if self.index < self.len {
87            let value = unsafe { self.query.get_unchecked(self.index) };
88            self.index += 1;
89            Some(value)
90        } else {
91            None
92        }
93    }
94}
95
96impl<'a> ExactSizeIterator for MDQueryIterator<'a> {
97    fn len(&self) -> usize {
98        (self.query.len() - self.index) as usize
99    }
100}
101
102declare_TCFType!{
103    MDQuery, MDQueryRef
104}
105impl_TCFType!(MDQuery, MDQueryRef, MDQueryGetTypeID);
106
107impl MDQuery {
108    pub fn new(query_string: CFString, value_list_attrs: Option<CFArray>, sorting_attrs: Option<CFArray>) -> Option<Self> {
109        unsafe {
110            let query_string = query_string.as_concrete_TypeRef();
111            let value_list_attrs = value_list_attrs.map(|v| v.as_concrete_TypeRef()).unwrap_or_else(::std::ptr::null);
112            let sorting_attrs = sorting_attrs.map(|v| v.as_concrete_TypeRef()).unwrap_or_else(::std::ptr::null);
113            let query_ref = MDQueryCreate(kCFAllocatorDefault, query_string, value_list_attrs, sorting_attrs);
114
115            if query_ref.is_null() {
116                None
117            } else {
118                Some(TCFType::wrap_under_create_rule(query_ref))
119            }
120        }
121    }
122
123    #[inline]
124    pub fn execute(&self, flags: MDQueryOptionFlags) -> bool {
125        unsafe {
126            MDQueryExecute(self.0, flags.bits())
127        }
128    }
129
130    #[inline]
131    pub fn stop(&self) {
132        unsafe {
133            MDQueryStop(self.0);
134        }
135    }
136
137    #[inline]
138    pub fn len(&self) -> CFIndex {
139        unsafe {
140            MDQueryGetResultCount(self.0)
141        }
142    }
143
144    #[inline]
145    pub(crate) unsafe fn get_unchecked<'a>(&'a self, idx: CFIndex) -> ItemRef<'a, MDItem> {
146        MDItem::from_void(MDQueryGetResultAtIndex(self.0, idx))
147    }
148
149    #[inline]
150    pub fn get<'a>(&'a self, idx: CFIndex) -> Option<ItemRef<'a, MDItem>> {
151        if idx < self.len() {
152            Some(unsafe { self.get_unchecked(idx) })
153        } else {
154            None
155        }
156    }
157
158    #[inline]
159    pub fn iter<'a>(&'a self) -> MDQueryIterator<'a> {
160        MDQueryIterator {
161            query: self,
162            index: 0,
163            len: self.len(),
164        }
165    }
166
167    #[inline]
168    pub fn query_string(&self) -> CFString {
169        unsafe {
170            TCFType::wrap_under_create_rule(MDQueryCopyQueryString(self.0))
171        }
172    }
173}
174
175impl ::std::fmt::Debug for MDQuery {
176    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
177        write!(f, "\"{:?}\"", self.query_string())
178    }
179}
180
181#[cfg(test)]
182mod tests {
183    use core_foundation::string::CFString;
184    use crate::mdquery::*;
185    use crate::mditem::attributes::Path;
186
187    #[test]
188    fn it_works() {
189        let query_string = "kMDItemContentTypeTree == \"com.apple.application\"c";
190        let query_cfstring = CFString::new(query_string);
191        let query = MDQuery::new(query_cfstring, None, None).unwrap();
192        query.execute(MDQueryOptionFlags::SYNC | MDQueryOptionFlags::ALLOW_FS_TRANSLATION);
193        println!("{:#?}", query.iter().map(|v| v.get(Path).unwrap()).collect::<Vec<_>>());
194    }
195}