1use std::path::PathBuf;
4
5use glib::{GString, StrVRef, prelude::*, subclass::prelude::*, translate::*};
6
7use libc::c_char;
8
9use crate::{File, Vfs, ffi};
10
11pub trait VfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<Vfs>> {
13 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 fn parse_name(&self, parse_name: &str) -> File {
46 self.parent_parse_name(parse_name)
47 }
48}
49
50pub 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
134unsafe 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 use super::*;
220 use crate::prelude::*;
221
222 mod imp {
224 use std::sync::LazyLock;
225
226 use super::*;
227
228 #[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 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 #[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 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 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 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
302 let active = my_custom_vfs.is_active();
303
304 let my_vfs = glib::Object::new::<MyVfs>();
306 let expected = my_vfs.is_active();
307
308 assert_eq!(active, expected);
310 }
311
312 #[test]
313 fn vfs_get_file_for_path() {
314 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
316 let file = my_custom_vfs.file_for_path("/path");
317
318 let my_vfs = glib::Object::new::<MyVfs>();
320 let expected = my_vfs.file_for_path("/path");
321
322 assert!(file.equal(&expected));
324 }
325
326 #[test]
327 fn vfs_get_file_for_uri() {
328 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
330 let file = my_custom_vfs.file_for_uri("file:///path");
331
332 let my_vfs = glib::Object::new::<MyVfs>();
334 let expected = my_vfs.file_for_uri("file:///path");
335
336 assert!(file.equal(&expected));
338 }
339
340 #[test]
341 fn vfs_get_supported_uri_schemes() {
342 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
344 let schemes = my_custom_vfs.supported_uri_schemes();
345
346 let my_vfs = glib::Object::new::<MyVfs>();
348 let expected = my_vfs.supported_uri_schemes();
349
350 assert_eq!(schemes, expected);
352 }
353
354 #[test]
355 fn vfs_parse_name() {
356 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
358 let file = my_custom_vfs.parse_name("file:///path");
359
360 let my_vfs = glib::Object::new::<MyVfs>();
362 let expected = my_vfs.parse_name("file:///path");
363
364 assert!(file.equal(&expected));
366 }
367}