1use std::path::PathBuf;
4
5use glib::{prelude::*, subclass::prelude::*, translate::*, GString, StrVRef};
6
7use libc::c_char;
8
9use crate::{ffi, File, Vfs};
10
11pub 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
34pub 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
118unsafe 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 use super::*;
194 use crate::prelude::*;
195
196 mod imp {
198 use std::sync::LazyLock;
199
200 use super::*;
201
202 #[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 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 #[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 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 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 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
276 let active = my_custom_vfs.is_active();
277
278 let my_vfs = glib::Object::new::<MyVfs>();
280 let expected = my_vfs.is_active();
281
282 assert_eq!(active, expected);
284 }
285
286 #[test]
287 fn vfs_get_file_for_path() {
288 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
290 let file = my_custom_vfs.file_for_path("/path");
291
292 let my_vfs = glib::Object::new::<MyVfs>();
294 let expected = my_vfs.file_for_path("/path");
295
296 assert!(file.equal(&expected));
298 }
299
300 #[test]
301 fn vfs_get_file_for_uri() {
302 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
304 let file = my_custom_vfs.file_for_uri("file:///path");
305
306 let my_vfs = glib::Object::new::<MyVfs>();
308 let expected = my_vfs.file_for_uri("file:///path");
309
310 assert!(file.equal(&expected));
312 }
313
314 #[test]
315 fn vfs_get_supported_uri_schemes() {
316 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
318 let schemes = my_custom_vfs.supported_uri_schemes();
319
320 let my_vfs = glib::Object::new::<MyVfs>();
322 let expected = my_vfs.supported_uri_schemes();
323
324 assert_eq!(schemes, expected);
326 }
327
328 #[test]
329 fn vfs_parse_name() {
330 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
332 let file = my_custom_vfs.parse_name("file:///path");
333
334 let my_vfs = glib::Object::new::<MyVfs>();
336 let expected = my_vfs.parse_name("file:///path");
337
338 assert!(file.equal(&expected));
340 }
341}