Skip to main content

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