gio/subclass/
vfs.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::path::PathBuf;
4
5use glib::{GString, StrVRef, prelude::*, subclass::prelude::*, translate::*};
6
7use libc::c_char;
8
9use crate::{File, Vfs, ffi};
10
11// Support custom implementation of virtual functions defined in `gio::ffi::GVfsClass`.
12pub trait VfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<Vfs>> {
13    /// Checks if the VFS is active.
14    ///
15    /// # Returns
16    ///
17    /// [`true`] if construction of the @self was successful
18    ///     and it is now active.
19    fn is_active(&self) -> bool {
20        self.parent_is_active()
21    }
22
23    fn get_file_for_path(&self, path: &std::path::Path) -> File {
24        self.parent_get_file_for_path(path)
25    }
26
27    fn get_file_for_uri(&self, uri: &str) -> File {
28        self.parent_get_file_for_uri(uri)
29    }
30
31    fn get_supported_uri_schemes(&self) -> &'static StrVRef {
32        self.parent_get_supported_uri_schemes()
33    }
34
35    /// This operation never fails, but the returned object might
36    /// not support any I/O operations if the @parse_name cannot
37    /// be parsed by the #GVfs module.
38    /// ## `parse_name`
39    /// a string to be parsed by the VFS module.
40    ///
41    /// # Returns
42    ///
43    /// a #GFile for the given @parse_name.
44    ///     Free the returned object with g_object_unref().
45    fn parse_name(&self, parse_name: &str) -> File {
46        self.parent_parse_name(parse_name)
47    }
48}
49
50// Support parent implementation of virtual functions defined in `gio::ffi::GVfsClass`.
51pub trait VfsImplExt: VfsImpl {
52    fn parent_is_active(&self) -> bool {
53        unsafe {
54            let data = Self::type_data();
55            let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
56
57            let f = (*parent_class)
58                .is_active
59                .expect("No parent class implementation for \"is_active\"");
60
61            let res = f(self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0);
62            from_glib(res)
63        }
64    }
65
66    fn parent_get_file_for_path(&self, path: &std::path::Path) -> File {
67        unsafe {
68            let data = Self::type_data();
69            let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
70
71            let f = (*parent_class)
72                .get_file_for_path
73                .expect("No parent class implementation for \"get_file_for_path\"");
74
75            let res = f(
76                self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
77                path.to_glib_none().0,
78            );
79            from_glib_full(res)
80        }
81    }
82
83    fn parent_get_file_for_uri(&self, uri: &str) -> File {
84        unsafe {
85            let data = Self::type_data();
86            let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
87
88            let f = (*parent_class)
89                .get_file_for_uri
90                .expect("No parent class implementation for \"get_file_for_uri\"");
91
92            let res = f(
93                self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
94                uri.to_glib_none().0,
95            );
96            from_glib_full(res)
97        }
98    }
99
100    fn parent_get_supported_uri_schemes(&self) -> &'static StrVRef {
101        unsafe {
102            let data = Self::type_data();
103            let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
104
105            let f = (*parent_class)
106                .get_supported_uri_schemes
107                .expect("No parent class implementation for \"get_supported_uri_schemes\"");
108
109            let res = f(self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0);
110            StrVRef::from_glib_borrow(res)
111        }
112    }
113
114    fn parent_parse_name(&self, parse_name: &str) -> File {
115        unsafe {
116            let data = Self::type_data();
117            let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
118
119            let f = (*parent_class)
120                .parse_name
121                .expect("No parent class implementation for \"parse_name\"");
122
123            let res = f(
124                self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
125                parse_name.to_glib_none().0,
126            );
127            from_glib_full(res)
128        }
129    }
130}
131
132impl<T: VfsImpl> VfsImplExt for T {}
133
134// Implement virtual functions defined in `gio::ffi::GVfsClass`.
135unsafe impl<T: VfsImpl> IsSubclassable<T> for Vfs {
136    fn class_init(class: &mut ::glib::Class<Self>) {
137        Self::parent_class_init::<T>(class);
138
139        let klass = class.as_mut();
140        klass.is_active = Some(is_active::<T>);
141        klass.get_file_for_path = Some(get_file_for_path::<T>);
142        klass.get_file_for_uri = Some(get_file_for_uri::<T>);
143        klass.get_supported_uri_schemes = Some(get_supported_uri_schemes::<T>);
144        klass.parse_name = Some(parse_name::<T>);
145    }
146}
147
148unsafe extern "C" fn is_active<T: VfsImpl>(vfs: *mut ffi::GVfs) -> glib::ffi::gboolean {
149    unsafe {
150        let instance = &*(vfs as *mut T::Instance);
151        let imp = instance.imp();
152
153        let res = imp.is_active();
154
155        res.into_glib()
156    }
157}
158
159unsafe extern "C" fn get_file_for_path<T: VfsImpl>(
160    vfs: *mut ffi::GVfs,
161    path: *const c_char,
162) -> *mut ffi::GFile {
163    unsafe {
164        let instance = &*(vfs as *mut T::Instance);
165        let imp = instance.imp();
166
167        let file = imp.get_file_for_path(&PathBuf::from_glib_none(path));
168
169        file.into_glib_ptr()
170    }
171}
172
173unsafe extern "C" fn get_file_for_uri<T: VfsImpl>(
174    vfs: *mut ffi::GVfs,
175    uri: *const c_char,
176) -> *mut ffi::GFile {
177    unsafe {
178        let instance = &*(vfs as *mut T::Instance);
179        let imp = instance.imp();
180
181        let file = imp.get_file_for_uri(&GString::from_glib_borrow(uri));
182
183        file.into_glib_ptr()
184    }
185}
186
187unsafe extern "C" fn get_supported_uri_schemes<T: VfsImpl>(
188    vfs: *mut ffi::GVfs,
189) -> *const *const c_char {
190    unsafe {
191        let instance = &*(vfs as *mut T::Instance);
192        let imp = instance.imp();
193
194        let supported_uri_schemes = imp.get_supported_uri_schemes();
195
196        supported_uri_schemes.as_ptr()
197    }
198}
199
200unsafe extern "C" fn parse_name<T: VfsImpl>(
201    vfs: *mut ffi::GVfs,
202    parse_name: *const c_char,
203) -> *mut ffi::GFile {
204    unsafe {
205        let instance = &*(vfs as *mut T::Instance);
206        let imp = instance.imp();
207
208        let file = imp.parse_name(&GString::from_glib_borrow(parse_name));
209
210        file.into_glib_ptr()
211    }
212}
213
214#[cfg(test)]
215mod tests {
216    // The following tests rely on a custom type `MyCustomVfs` that extends another custom type `MyVfs`.
217    // For each virtual method defined in class `gio::ffi::GVfsClass`, a test checks that `MyCustomVfs` and `MyVfs` return the same results.
218
219    use super::*;
220    use crate::prelude::*;
221
222    // Define `MyCustomVfs` as a subclass of `MyVfs`.
223    mod imp {
224        use std::sync::LazyLock;
225
226        use super::*;
227
228        // Defines `MyVfs` as a subclass of `Vfs`.
229        #[derive(Default)]
230        pub struct MyVfs;
231
232        #[glib::object_subclass]
233        impl ObjectSubclass for MyVfs {
234            const NAME: &'static str = "MyVfs";
235            type Type = super::MyVfs;
236            type ParentType = Vfs;
237        }
238
239        impl ObjectImpl for MyVfs {}
240
241        // Implements `VfsImpl` with custom implementation.
242        impl VfsImpl for MyVfs {
243            fn is_active(&self) -> bool {
244                true
245            }
246
247            fn get_file_for_path(&self, path: &std::path::Path) -> File {
248                File::for_path(path)
249            }
250
251            fn get_file_for_uri(&self, uri: &str) -> File {
252                File::for_uri(uri)
253            }
254
255            fn get_supported_uri_schemes(&self) -> &'static StrVRef {
256                static SUPPORTED_URI_SCHEMES: LazyLock<glib::StrV> =
257                    LazyLock::new(|| glib::StrV::from(["file"]));
258                &SUPPORTED_URI_SCHEMES
259            }
260
261            fn parse_name(&self, parse_name: &str) -> File {
262                File::for_parse_name(parse_name)
263            }
264        }
265
266        // Defines `MyCustomVfs` as a subclass of `MyVfs`.
267        #[derive(Default)]
268        pub struct MyCustomVfs;
269
270        #[glib::object_subclass]
271        impl ObjectSubclass for MyCustomVfs {
272            const NAME: &'static str = "MyCustomVfs";
273            type Type = super::MyCustomVfs;
274            type ParentType = super::MyVfs;
275        }
276
277        impl ObjectImpl for MyCustomVfs {}
278
279        // Implements `VfsImpl` with default implementation, which calls the parent's implementation.
280        impl VfsImpl for MyCustomVfs {}
281
282        impl MyVfsImpl for MyCustomVfs {}
283    }
284
285    glib::wrapper! {
286        pub struct MyVfs(ObjectSubclass<imp::MyVfs>) @extends Vfs;
287    }
288
289    pub trait MyVfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<MyVfs> + IsA<Vfs>> {}
290
291    // To make this class subclassable we need to implement IsSubclassable
292    unsafe impl<T: MyVfsImpl + VfsImpl> IsSubclassable<T> for MyVfs {}
293
294    glib::wrapper! {
295        pub struct MyCustomVfs(ObjectSubclass<imp::MyCustomVfs>) @extends MyVfs, Vfs;
296    }
297
298    #[test]
299    fn vfs_is_active() {
300        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::is_active`
301        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
302        let active = my_custom_vfs.is_active();
303
304        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::is_active`
305        let my_vfs = glib::Object::new::<MyVfs>();
306        let expected = my_vfs.is_active();
307
308        // both results should equal
309        assert_eq!(active, expected);
310    }
311
312    #[test]
313    fn vfs_get_file_for_path() {
314        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
315        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
316        let file = my_custom_vfs.file_for_path("/path");
317
318        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
319        let my_vfs = glib::Object::new::<MyVfs>();
320        let expected = my_vfs.file_for_path("/path");
321
322        // both files should equal
323        assert!(file.equal(&expected));
324    }
325
326    #[test]
327    fn vfs_get_file_for_uri() {
328        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
329        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
330        let file = my_custom_vfs.file_for_uri("file:///path");
331
332        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
333        let my_vfs = glib::Object::new::<MyVfs>();
334        let expected = my_vfs.file_for_uri("file:///path");
335
336        // both files should equal
337        assert!(file.equal(&expected));
338    }
339
340    #[test]
341    fn vfs_get_supported_uri_schemes() {
342        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
343        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
344        let schemes = my_custom_vfs.supported_uri_schemes();
345
346        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
347        let my_vfs = glib::Object::new::<MyVfs>();
348        let expected = my_vfs.supported_uri_schemes();
349
350        // both results should equal
351        assert_eq!(schemes, expected);
352    }
353
354    #[test]
355    fn vfs_parse_name() {
356        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::parse_name`
357        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
358        let file = my_custom_vfs.parse_name("file:///path");
359
360        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::parse_name`
361        let my_vfs = glib::Object::new::<MyVfs>();
362        let expected = my_vfs.parse_name("file:///path");
363
364        // both files should equal
365        assert!(file.equal(&expected));
366    }
367}