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::{prelude::*, subclass::prelude::*, translate::*, GString, StrVRef};
6
7use libc::c_char;
8
9use crate::{ffi, File, Vfs};
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    let instance = &*(vfs as *mut T::Instance);
134    let imp = instance.imp();
135
136    let res = imp.is_active();
137
138    res.into_glib()
139}
140
141unsafe extern "C" fn get_file_for_path<T: VfsImpl>(
142    vfs: *mut ffi::GVfs,
143    path: *const c_char,
144) -> *mut ffi::GFile {
145    let instance = &*(vfs as *mut T::Instance);
146    let imp = instance.imp();
147
148    let file = imp.get_file_for_path(&PathBuf::from_glib_none(path));
149
150    file.into_glib_ptr()
151}
152
153unsafe extern "C" fn get_file_for_uri<T: VfsImpl>(
154    vfs: *mut ffi::GVfs,
155    uri: *const c_char,
156) -> *mut ffi::GFile {
157    let instance = &*(vfs as *mut T::Instance);
158    let imp = instance.imp();
159
160    let file = imp.get_file_for_uri(&GString::from_glib_borrow(uri));
161
162    file.into_glib_ptr()
163}
164
165unsafe extern "C" fn get_supported_uri_schemes<T: VfsImpl>(
166    vfs: *mut ffi::GVfs,
167) -> *const *const c_char {
168    let instance = &*(vfs as *mut T::Instance);
169    let imp = instance.imp();
170
171    let supported_uri_schemes = imp.get_supported_uri_schemes();
172
173    supported_uri_schemes.as_ptr()
174}
175
176unsafe extern "C" fn parse_name<T: VfsImpl>(
177    vfs: *mut ffi::GVfs,
178    parse_name: *const c_char,
179) -> *mut ffi::GFile {
180    let instance = &*(vfs as *mut T::Instance);
181    let imp = instance.imp();
182
183    let file = imp.parse_name(&GString::from_glib_borrow(parse_name));
184
185    file.into_glib_ptr()
186}
187
188#[cfg(test)]
189mod tests {
190    // The following tests rely on a custom type `MyCustomVfs` that extends another custom type `MyVfs`.
191    // For each virtual method defined in class `gio::ffi::GVfsClass`, a test checks that `MyCustomVfs` and `MyVfs` return the same results.
192
193    use super::*;
194    use crate::prelude::*;
195
196    // Define `MyCustomVfs` as a subclass of `MyVfs`.
197    mod imp {
198        use std::sync::LazyLock;
199
200        use super::*;
201
202        // Defines `MyVfs` as a subclass of `Vfs`.
203        #[derive(Default)]
204        pub struct MyVfs;
205
206        #[glib::object_subclass]
207        impl ObjectSubclass for MyVfs {
208            const NAME: &'static str = "MyVfs";
209            type Type = super::MyVfs;
210            type ParentType = Vfs;
211        }
212
213        impl ObjectImpl for MyVfs {}
214
215        // Implements `VfsImpl` with custom implementation.
216        impl VfsImpl for MyVfs {
217            fn is_active(&self) -> bool {
218                true
219            }
220
221            fn get_file_for_path(&self, path: &std::path::Path) -> File {
222                File::for_path(path)
223            }
224
225            fn get_file_for_uri(&self, uri: &str) -> File {
226                File::for_uri(uri)
227            }
228
229            fn get_supported_uri_schemes(&self) -> &'static StrVRef {
230                static SUPPORTED_URI_SCHEMES: LazyLock<glib::StrV> =
231                    LazyLock::new(|| glib::StrV::from(["file"]));
232                &SUPPORTED_URI_SCHEMES
233            }
234
235            fn parse_name(&self, parse_name: &str) -> File {
236                File::for_parse_name(parse_name)
237            }
238        }
239
240        // Defines `MyCustomVfs` as a subclass of `MyVfs`.
241        #[derive(Default)]
242        pub struct MyCustomVfs;
243
244        #[glib::object_subclass]
245        impl ObjectSubclass for MyCustomVfs {
246            const NAME: &'static str = "MyCustomVfs";
247            type Type = super::MyCustomVfs;
248            type ParentType = super::MyVfs;
249        }
250
251        impl ObjectImpl for MyCustomVfs {}
252
253        // Implements `VfsImpl` with default implementation, which calls the parent's implementation.
254        impl VfsImpl for MyCustomVfs {}
255
256        impl MyVfsImpl for MyCustomVfs {}
257    }
258
259    glib::wrapper! {
260        pub struct MyVfs(ObjectSubclass<imp::MyVfs>) @extends Vfs;
261    }
262
263    pub trait MyVfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<MyVfs> + IsA<Vfs>> {}
264
265    // To make this class subclassable we need to implement IsSubclassable
266    unsafe impl<T: MyVfsImpl + VfsImpl> IsSubclassable<T> for MyVfs {}
267
268    glib::wrapper! {
269        pub struct MyCustomVfs(ObjectSubclass<imp::MyCustomVfs>) @extends MyVfs, Vfs;
270    }
271
272    #[test]
273    fn vfs_is_active() {
274        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::is_active`
275        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
276        let active = my_custom_vfs.is_active();
277
278        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::is_active`
279        let my_vfs = glib::Object::new::<MyVfs>();
280        let expected = my_vfs.is_active();
281
282        // both results should equal
283        assert_eq!(active, expected);
284    }
285
286    #[test]
287    fn vfs_get_file_for_path() {
288        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
289        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
290        let file = my_custom_vfs.file_for_path("/path");
291
292        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
293        let my_vfs = glib::Object::new::<MyVfs>();
294        let expected = my_vfs.file_for_path("/path");
295
296        // both files should equal
297        assert!(file.equal(&expected));
298    }
299
300    #[test]
301    fn vfs_get_file_for_uri() {
302        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
303        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
304        let file = my_custom_vfs.file_for_uri("file:///path");
305
306        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
307        let my_vfs = glib::Object::new::<MyVfs>();
308        let expected = my_vfs.file_for_uri("file:///path");
309
310        // both files should equal
311        assert!(file.equal(&expected));
312    }
313
314    #[test]
315    fn vfs_get_supported_uri_schemes() {
316        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
317        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
318        let schemes = my_custom_vfs.supported_uri_schemes();
319
320        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
321        let my_vfs = glib::Object::new::<MyVfs>();
322        let expected = my_vfs.supported_uri_schemes();
323
324        // both results should equal
325        assert_eq!(schemes, expected);
326    }
327
328    #[test]
329    fn vfs_parse_name() {
330        // invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::parse_name`
331        let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
332        let file = my_custom_vfs.parse_name("file:///path");
333
334        // invoke `MyVfs` implementation of `gio::ffi::GVfsClass::parse_name`
335        let my_vfs = glib::Object::new::<MyVfs>();
336        let expected = my_vfs.parse_name("file:///path");
337
338        // both files should equal
339        assert!(file.equal(&expected));
340    }
341}