1use glib_ffi::gpointer;
2use gobject_ffi::{GClosure, g_closure_ref};
3use nemo_ffi::{NemoFileInfo, NemoInfoProvider, NemoOperationHandle, NemoOperationResult};
4use nemo_ffi::{nemo_file_info_add_string_attribute, nemo_file_info_get_uri, nemo_file_info_get_uri_scheme};
5use nemo_ffi::nemo_file_info_invalidate_extension_info;
6use std::collections::HashMap;
7use std::ffi::{CStr, CString};
8use std::sync::{Arc, Mutex};
9use std::sync::atomic::{AtomicUsize, Ordering};
10
11pub trait InfoProvider : Send + Sync {
12 fn should_update_file_info(&self, &FileInfo) -> bool;
13 fn update_file_info(&self, &mut FileInfo);
14}
15
16#[derive(Clone)]
17pub struct FileInfo {
18 pub raw_file_info: *mut NemoFileInfo,
19 pub attributes: HashMap<String, String>,
20}
21
22unsafe impl Send for FileInfo {}
23
24impl FileInfo {
25 pub fn new(raw_file_info: *mut NemoFileInfo) -> FileInfo {
26 FileInfo {
27 raw_file_info: raw_file_info,
28 attributes: HashMap::new(),
29 }
30 }
31
32 pub fn get_uri(&self) -> String {
33 let raw_file_info = &self.raw_file_info;
34 unsafe {
35 CStr::from_ptr(nemo_file_info_get_uri(*raw_file_info)).to_string_lossy().into_owned()
36 }
37 }
38
39 pub fn get_uri_scheme(&self) -> String {
40 let raw_file_info = &self.raw_file_info;
41 unsafe {
42 CStr::from_ptr(nemo_file_info_get_uri_scheme(*raw_file_info)).to_string_lossy().into_owned()
43 }
44 }
45
46 pub fn invalidate_extension_info(&self) {
47 unsafe {
48 nemo_file_info_invalidate_extension_info(self.raw_file_info);
49 }
50 }
51
52 pub fn add_attribute(&mut self, name: &str, value: &str) -> &mut FileInfo {
53 self.attributes.insert(name.to_string(), value.to_string());
54 self
55 }
56}
57
58pub struct UpdateFileInfoOperationHandle {
59 pub skip_response: bool
60}
61
62macro_rules! info_provider_iface {
63 ($iface_init_fn:ident, $update_file_info_fn:ident, $update_file_info_bg_fn:ident, $cancel_update_fn:ident, $rust_provider:ident, $set_rust_provider:ident) => {
64 #[no_mangle]
65 pub unsafe extern "C" fn $iface_init_fn(iface: gpointer, _: gpointer) {
66 use nemo_ffi::NemoInfoProviderIface;
67
68 let iface_struct = iface as *mut NemoInfoProviderIface;
69 (*iface_struct).update_file_info = Some($update_file_info_fn);
70 (*iface_struct).cancel_update = Some($cancel_update_fn);
71 }
72
73 #[no_mangle]
74 pub extern "C" fn $update_file_info_fn(provider: *mut NemoInfoProvider,
75 file: *mut NemoFileInfo,
76 update_complete: *mut GClosure,
77 handle: *mut *mut NemoOperationHandle) -> NemoOperationResult {
78 use std::mem;
79 use std::sync::{Arc, Mutex};
80 use std::sync::mpsc::channel;
81 use std::thread;
82
83 let mut file_info = FileInfo::new(file);
84
85 let should_update_file_info = match *$rust_provider.lock().unwrap() {
86 Some(ref p) => p.should_update_file_info(&mut file_info),
87 None => false,
88 };
89
90 if !should_update_file_info {
91 return NemoOperationResult::NemoOperationComplete;
92 }
93
94 let my_handle = Arc::new(Mutex::new(UpdateFileInfoOperationHandle { skip_response: false }));
95 let my_handle_local = my_handle.clone();
96 let my_handle_thread = my_handle.clone();
97
98 let (tx, rx) = channel();
99 thread::spawn(move || {
100 let (file_info, provider, update_complete, handle_ref) = rx.recv().unwrap();
101 $update_file_info_bg_fn(file_info, provider, update_complete, my_handle_thread, handle_ref);
102 });
103
104 unsafe {
105 let closure_copy = g_closure_ref(update_complete);
106 *handle = mem::transmute(Box::into_raw(Box::new(my_handle_local)));
107 tx.send((Box::new(file_info), &mut *provider, &mut *closure_copy, &mut **handle)).unwrap();
108 }
109
110 return NemoOperationResult::NemoOperationInProgress;
111 }
112
113 #[no_mangle]
114 pub extern "C" fn $cancel_update_fn(_provider: *mut NemoInfoProvider, handle: *mut NemoOperationHandle) {
115 use std::sync::{Arc, Mutex};
116
117 unsafe {
118 let handle = handle as *mut Arc<Mutex<UpdateFileInfoOperationHandle>>;
119 let mut my_handle = (*handle).lock().unwrap();
120 my_handle.skip_response = true;
121 }
122 }
123
124 fn $update_file_info_bg_fn(mut file_info: Box<FileInfo>,
125 provider: &mut NemoInfoProvider,
126 update_complete: &mut GClosure,
127 my_handle: Arc<Mutex<UpdateFileInfoOperationHandle>>,
128 handle_ref: &mut NemoOperationHandle) {
129 use nemo_ffi::nemo_info_provider_update_complete_invoke;
130
131 match *$rust_provider.lock().unwrap() {
132 Some(ref p) => p.update_file_info(file_info.as_mut()),
133 None => (),
134 };
135
136 if !my_handle.lock().unwrap().skip_response {
137 let file_info = file_info.as_mut();
138 let attributes = &file_info.attributes;
139 unsafe {
140 for (attr_name, attr_value) in attributes {
141 let attr_name_c = CString::new(attr_name.as_str()).unwrap().into_raw();
142 let attr_value_c = CString::new(attr_value.as_str()).unwrap().into_raw();
143
144 nemo_file_info_add_string_attribute(file_info.raw_file_info, attr_name_c, attr_value_c);
145
146 CString::from_raw(attr_name_c);
148 CString::from_raw(attr_value_c);
149 }
150
151 nemo_info_provider_update_complete_invoke(update_complete, provider, handle_ref, NemoOperationResult::NemoOperationComplete);
152 }
153 }
154 }
155
156 pub fn $set_rust_provider(info_provider: Box<dyn InfoProvider>) {
157 *$rust_provider.lock().unwrap() = Some(info_provider);
158 }
159
160 lazy_static! {
161 static ref $rust_provider: Mutex<Option<Box<dyn InfoProvider>>> = Mutex::new(None);
162 }
163 }
164}
165
166info_provider_iface!(info_provider_iface_init_0, info_provider_update_file_info_0, info_provider_update_file_info_bg_0, info_provider_cancel_update_0, INFO_PROVIDER_0, set_info_provider_0);
167info_provider_iface!(info_provider_iface_init_1, info_provider_update_file_info_1, info_provider_update_file_info_bg_1, info_provider_cancel_update_1, INFO_PROVIDER_1, set_info_provider_1);
168info_provider_iface!(info_provider_iface_init_2, info_provider_update_file_info_2, info_provider_update_file_info_bg_2, info_provider_cancel_update_2, INFO_PROVIDER_2, set_info_provider_2);
169info_provider_iface!(info_provider_iface_init_3, info_provider_update_file_info_3, info_provider_update_file_info_bg_3, info_provider_cancel_update_3, INFO_PROVIDER_3, set_info_provider_3);
170info_provider_iface!(info_provider_iface_init_4, info_provider_update_file_info_4, info_provider_update_file_info_bg_4, info_provider_cancel_update_4, INFO_PROVIDER_4, set_info_provider_4);
171info_provider_iface!(info_provider_iface_init_5, info_provider_update_file_info_5, info_provider_update_file_info_bg_5, info_provider_cancel_update_5, INFO_PROVIDER_5, set_info_provider_5);
172info_provider_iface!(info_provider_iface_init_6, info_provider_update_file_info_6, info_provider_update_file_info_bg_6, info_provider_cancel_update_6, INFO_PROVIDER_6, set_info_provider_6);
173info_provider_iface!(info_provider_iface_init_7, info_provider_update_file_info_7, info_provider_update_file_info_bg_7, info_provider_cancel_update_7, INFO_PROVIDER_7, set_info_provider_7);
174info_provider_iface!(info_provider_iface_init_8, info_provider_update_file_info_8, info_provider_update_file_info_bg_8, info_provider_cancel_update_8, INFO_PROVIDER_8, set_info_provider_8);
175info_provider_iface!(info_provider_iface_init_9, info_provider_update_file_info_9, info_provider_update_file_info_bg_9, info_provider_cancel_update_9, INFO_PROVIDER_9, set_info_provider_9);
176
177pub fn info_provider_iface_externs() -> Vec<unsafe extern "C" fn(gpointer, gpointer)> {
178 vec![
179 info_provider_iface_init_0,
180 info_provider_iface_init_1,
181 info_provider_iface_init_2,
182 info_provider_iface_init_3,
183 info_provider_iface_init_4,
184 info_provider_iface_init_5,
185 info_provider_iface_init_6,
186 info_provider_iface_init_7,
187 info_provider_iface_init_8,
188 info_provider_iface_init_9,
189 ]
190}
191
192pub fn rust_info_provider_setters() -> Vec<fn(Box<dyn InfoProvider>)> {
193 vec![
194 set_info_provider_0,
195 set_info_provider_1,
196 set_info_provider_2,
197 set_info_provider_3,
198 set_info_provider_4,
199 set_info_provider_5,
200 set_info_provider_6,
201 set_info_provider_7,
202 set_info_provider_8,
203 set_info_provider_9,
204 ]
205}
206
207static NEXT_INFO_PROVIDER_IFACE_INDEX: AtomicUsize = AtomicUsize::new(0);
208
209pub fn take_next_info_provider_iface_index() -> usize {
210 NEXT_INFO_PROVIDER_IFACE_INDEX.fetch_add(1, Ordering::SeqCst)
211}