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 {
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 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 use super::*;
204 use crate::prelude::*;
205
206 mod imp {
208 use std::sync::LazyLock;
209
210 use super::*;
211
212 #[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 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 #[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 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 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 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
286 let active = my_custom_vfs.is_active();
287
288 let my_vfs = glib::Object::new::<MyVfs>();
290 let expected = my_vfs.is_active();
291
292 assert_eq!(active, expected);
294 }
295
296 #[test]
297 fn vfs_get_file_for_path() {
298 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
300 let file = my_custom_vfs.file_for_path("/path");
301
302 let my_vfs = glib::Object::new::<MyVfs>();
304 let expected = my_vfs.file_for_path("/path");
305
306 assert!(file.equal(&expected));
308 }
309
310 #[test]
311 fn vfs_get_file_for_uri() {
312 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
314 let file = my_custom_vfs.file_for_uri("file:///path");
315
316 let my_vfs = glib::Object::new::<MyVfs>();
318 let expected = my_vfs.file_for_uri("file:///path");
319
320 assert!(file.equal(&expected));
322 }
323
324 #[test]
325 fn vfs_get_supported_uri_schemes() {
326 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
328 let schemes = my_custom_vfs.supported_uri_schemes();
329
330 let my_vfs = glib::Object::new::<MyVfs>();
332 let expected = my_vfs.supported_uri_schemes();
333
334 assert_eq!(schemes, expected);
336 }
337
338 #[test]
339 fn vfs_parse_name() {
340 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
342 let file = my_custom_vfs.parse_name("file:///path");
343
344 let my_vfs = glib::Object::new::<MyVfs>();
346 let expected = my_vfs.parse_name("file:///path");
347
348 assert!(file.equal(&expected));
350 }
351}