gio/subclass/
file_enumerator.rs1use glib::{prelude::*, subclass::prelude::*, translate::*};
4
5use crate::{ffi, prelude::*, Cancellable, FileEnumerator, FileInfo, IOErrorEnum};
6
7pub trait FileEnumeratorImpl: ObjectImpl + ObjectSubclass<Type: IsA<FileEnumerator>> {
10 fn next_file(
11 &self,
12 cancellable: Option<&Cancellable>,
13 ) -> Result<Option<FileInfo>, glib::Error> {
14 self.parent_next_file(cancellable)
15 }
16
17 fn close(&self, cancellable: Option<&Cancellable>) -> (bool, Option<glib::Error>) {
48 self.parent_close(cancellable)
49 }
50}
51
52pub trait FileEnumeratorImplExt: FileEnumeratorImpl {
55 fn parent_next_file(
56 &self,
57 cancellable: Option<&Cancellable>,
58 ) -> Result<Option<FileInfo>, glib::Error> {
59 if self.obj().is_closed() {
60 Err(glib::Error::new::<IOErrorEnum>(
61 IOErrorEnum::Closed,
62 "Enumerator is closed",
63 ))
64 } else {
65 unsafe {
66 let data = Self::type_data();
67 let parent_class = data.as_ref().parent_class() as *const ffi::GFileEnumeratorClass;
68
69 let f = (*parent_class)
70 .next_file
71 .expect("No parent class implementation for \"next_file\"");
72
73 let mut error = std::ptr::null_mut();
74 let res = f(
75 self.obj()
76 .unsafe_cast_ref::<FileEnumerator>()
77 .to_glib_none()
78 .0,
79 cancellable.as_ref().to_glib_none().0,
80 &mut error,
81 );
82 if error.is_null() {
83 Ok(from_glib_full(res))
84 } else {
85 Err(from_glib_full(error))
86 }
87 }
88 }
89 }
90
91 fn parent_close(&self, cancellable: Option<&Cancellable>) -> (bool, Option<glib::Error>) {
92 unsafe {
93 let data = Self::type_data();
94 let parent_class = data.as_ref().parent_class() as *const ffi::GFileEnumeratorClass;
95
96 let f = (*parent_class)
97 .close_fn
98 .expect("No parent class implementation for \"close_fn\"");
99
100 let mut error = std::ptr::null_mut();
101 let is_ok = f(
102 self.obj()
103 .unsafe_cast_ref::<FileEnumerator>()
104 .to_glib_none()
105 .0,
106 cancellable.as_ref().to_glib_none().0,
107 &mut error,
108 );
109 (from_glib(is_ok), from_glib_full(error))
110 }
111 }
112}
113
114impl<T: FileEnumeratorImpl> FileEnumeratorImplExt for T {}
115
116unsafe impl<T: FileEnumeratorImpl> IsSubclassable<T> for FileEnumerator {
118 fn class_init(class: &mut ::glib::Class<Self>) {
119 Self::parent_class_init::<T>(class);
120
121 let klass = class.as_mut();
122 klass.next_file = Some(next_file::<T>);
123 klass.close_fn = Some(close_fn::<T>);
124 }
131}
132
133unsafe extern "C" fn next_file<T: FileEnumeratorImpl>(
134 enumerator: *mut ffi::GFileEnumerator,
135 cancellable: *mut ffi::GCancellable,
136 error: *mut *mut glib::ffi::GError,
137) -> *mut ffi::GFileInfo {
138 let instance = &*(enumerator as *mut T::Instance);
139 let imp = instance.imp();
140 let cancellable = Option::<Cancellable>::from_glib_none(cancellable);
141
142 let res = imp.next_file(cancellable.as_ref());
143
144 match res {
145 Ok(fileinfo) => fileinfo.to_glib_full(),
146 Err(err) => {
147 if !error.is_null() {
148 *error = err.to_glib_full()
149 }
150 std::ptr::null_mut()
151 }
152 }
153}
154
155unsafe extern "C" fn close_fn<T: FileEnumeratorImpl>(
156 enumerator: *mut ffi::GFileEnumerator,
157 cancellable: *mut ffi::GCancellable,
158 error: *mut *mut glib::ffi::GError,
159) -> glib::ffi::gboolean {
160 let instance = &*(enumerator as *mut T::Instance);
161 let imp = instance.imp();
162 let cancellable = Option::<Cancellable>::from_glib_none(cancellable);
163
164 let res = imp.close(cancellable.as_ref());
165
166 if !error.is_null() {
167 *error = res.1.to_glib_full()
168 }
169
170 res.0.into_glib()
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
179
180 mod imp {
182 use std::cell::Cell;
183
184 use super::*;
185
186 #[derive(Default)]
187 pub struct MyFileEnumerator(Cell<i32>);
188
189 #[glib::object_subclass]
190 impl ObjectSubclass for MyFileEnumerator {
191 const NAME: &'static str = "MyFileEnumerator";
192 type Type = super::MyFileEnumerator;
193 type ParentType = FileEnumerator;
194 }
195
196 impl ObjectImpl for MyFileEnumerator {
197 fn dispose(&self) {
198 let _ = self.obj().close(Cancellable::NONE);
199 }
200 }
201
202 impl FileEnumeratorImpl for MyFileEnumerator {
204 fn next_file(
205 &self,
206 cancellable: Option<&Cancellable>,
207 ) -> Result<Option<FileInfo>, glib::Error> {
208 if cancellable.is_some_and(|c| c.is_cancelled()) {
209 Err(glib::Error::new::<IOErrorEnum>(
210 IOErrorEnum::Cancelled,
211 "Operation was cancelled",
212 ))
213 } else {
214 match self.0.get() {
215 -1 => Err(glib::Error::new::<IOErrorEnum>(
216 IOErrorEnum::Closed,
217 "Enumerator is closed",
218 )),
219 i if i < 3 => {
220 let file_info = FileInfo::new();
221 file_info.set_display_name(&format!("file{i}"));
222 self.0.set(i + 1);
223 Ok(Some(file_info))
224 }
225 _ => Ok(None),
226 }
227 }
228 }
229
230 fn close(&self, cancellable: Option<&Cancellable>) -> (bool, Option<glib::Error>) {
231 if cancellable.is_some_and(|c| c.is_cancelled()) {
232 (
233 false,
234 Some(glib::Error::new::<IOErrorEnum>(
235 IOErrorEnum::Cancelled,
236 "Operation was cancelled",
237 )),
238 )
239 } else {
240 self.0.set(-1);
241 (true, None)
242 }
243 }
244 }
245
246 #[derive(Default)]
247 pub struct MyCustomFileEnumerator;
248
249 #[glib::object_subclass]
250 impl ObjectSubclass for MyCustomFileEnumerator {
251 const NAME: &'static str = "MyCustomFileEnumerator";
252 type Type = super::MyCustomFileEnumerator;
253 type ParentType = super::MyFileEnumerator;
254 }
255
256 impl ObjectImpl for MyCustomFileEnumerator {}
257
258 impl FileEnumeratorImpl for MyCustomFileEnumerator {}
260
261 impl MyFileEnumeratorImpl for MyCustomFileEnumerator {}
262 }
263
264 glib::wrapper! {
265 pub struct MyFileEnumerator(ObjectSubclass<imp::MyFileEnumerator>) @extends FileEnumerator;
266 }
267
268 pub trait MyFileEnumeratorImpl:
269 ObjectImpl + ObjectSubclass<Type: IsA<MyFileEnumerator> + IsA<FileEnumerator>>
270 {
271 }
272
273 unsafe impl<T: MyFileEnumeratorImpl + FileEnumeratorImpl> IsSubclassable<T> for MyFileEnumerator {}
275
276 glib::wrapper! {
277 pub struct MyCustomFileEnumerator(ObjectSubclass<imp::MyCustomFileEnumerator>) @extends MyFileEnumerator, FileEnumerator;
278 }
279
280 #[test]
281 fn file_enumerator_next_file() {
282 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
284 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
285 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
286 let filename = res.unwrap().unwrap().display_name();
287
288 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
290 let res = my_file_enumerator.next_file(Cancellable::NONE);
291 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
292 let expected = res.unwrap().unwrap().display_name();
293
294 assert_eq!(filename, expected);
296
297 for res in my_custom_file_enumerator.upcast::<FileEnumerator>() {
299 assert!(res.as_ref().is_ok());
300 let filename = res.unwrap().display_name();
301
302 let res = my_file_enumerator.next_file(Cancellable::NONE);
303 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
304 let expected = res.unwrap().unwrap().display_name();
305
306 assert_eq!(filename, expected);
308 }
309 }
310
311 #[test]
312 fn file_enumerator_close() {
313 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
315 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
316 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
317 let filename = res.unwrap().unwrap().display_name();
318
319 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
321 let res = my_file_enumerator.next_file(Cancellable::NONE);
322 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
323 let expected = res.unwrap().unwrap().display_name();
324
325 assert_eq!(filename, expected);
327
328 let res = my_custom_file_enumerator.close(Cancellable::NONE);
330 assert_eq!(res.1, None);
331 let closed = res.0;
332
333 let res = my_file_enumerator.close(Cancellable::NONE);
335 assert_eq!(res.1, None);
336 let expected = res.0;
337
338 assert_eq!(closed, expected);
340
341 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
343 assert!(res.is_err());
344 let err = res.unwrap_err();
345
346 let res = my_file_enumerator.next_file(Cancellable::NONE);
348 assert!(res.is_err());
349 let expected = res.unwrap_err();
350
351 assert_eq!(err.domain(), expected.domain());
353 assert!(err.matches::<IOErrorEnum>(IOErrorEnum::Closed));
354 assert!(expected.matches::<IOErrorEnum>(IOErrorEnum::Closed));
355 assert_eq!(err.message(), expected.message());
356 }
357
358 #[test]
359 fn file_enumerator_cancel() {
360 let my_custom_file_enumerator = glib::Object::new::<MyCustomFileEnumerator>();
362 let res = my_custom_file_enumerator.next_file(Cancellable::NONE);
363 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
364 let filename = res.unwrap().unwrap().display_name();
365
366 let my_file_enumerator = glib::Object::new::<MyFileEnumerator>();
368 let res = my_file_enumerator.next_file(Cancellable::NONE);
369 assert!(res.as_ref().is_ok_and(|res| res.is_some()));
370 let expected = res.unwrap().unwrap().display_name();
371
372 assert_eq!(filename, expected);
374
375 let cancellable = Cancellable::new();
377 cancellable.cancel();
378 let res = my_custom_file_enumerator.next_file(Some(&cancellable));
379 assert!(res.as_ref().is_err());
380 let err = res.unwrap_err();
381
382 let cancellable = Cancellable::new();
384 cancellable.cancel();
385 let res = my_file_enumerator.next_file(Some(&cancellable));
386 assert!(res.as_ref().is_err());
387 let expected = res.unwrap_err();
388
389 assert_eq!(err.domain(), expected.domain());
391 assert!(err.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
392 assert!(expected.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
393 assert_eq!(err.message(), expected.message());
394
395 let cancellable = Cancellable::new();
397 cancellable.cancel();
398 let res = my_custom_file_enumerator.close(Some(&cancellable));
399 assert!(res.1.is_some());
400 let err = res.1.unwrap();
401
402 let cancellable = Cancellable::new();
404 cancellable.cancel();
405 let res = my_file_enumerator.close(Some(&cancellable));
406 assert!(res.1.is_some());
407 let expected = res.1.unwrap();
408
409 assert_eq!(err.domain(), expected.domain());
411 assert!(err.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
412 assert!(expected.matches::<IOErrorEnum>(IOErrorEnum::Cancelled));
413 assert_eq!(err.message(), expected.message());
414 }
415}