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#[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 MDQueryCopyQueryString(query: MDQueryRef) -> CFStringRef;
57
58
59 fn MDQueryExecute(query: MDQueryRef, optionFlags: CFOptionFlags) -> bool;
60 fn MDQueryStop(query: MDQueryRef);
61 fn MDQueryGetResultAtIndex(query: MDQueryRef, idx: CFIndex) -> *const c_void;
70 fn MDQueryGetResultCount(query: MDQueryRef) -> CFIndex;
71 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}