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 {
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 let instance = &*(vfs as *mut T::Instance);
150 let imp = instance.imp();
151
152 let res = imp.is_active();
153
154 res.into_glib()
155}
156
157unsafe extern "C" fn get_file_for_path<T: VfsImpl>(
158 vfs: *mut ffi::GVfs,
159 path: *const c_char,
160) -> *mut ffi::GFile {
161 let instance = &*(vfs as *mut T::Instance);
162 let imp = instance.imp();
163
164 let file = imp.get_file_for_path(&PathBuf::from_glib_none(path));
165
166 file.into_glib_ptr()
167}
168
169unsafe extern "C" fn get_file_for_uri<T: VfsImpl>(
170 vfs: *mut ffi::GVfs,
171 uri: *const c_char,
172) -> *mut ffi::GFile {
173 let instance = &*(vfs as *mut T::Instance);
174 let imp = instance.imp();
175
176 let file = imp.get_file_for_uri(&GString::from_glib_borrow(uri));
177
178 file.into_glib_ptr()
179}
180
181unsafe extern "C" fn get_supported_uri_schemes<T: VfsImpl>(
182 vfs: *mut ffi::GVfs,
183) -> *const *const c_char {
184 let instance = &*(vfs as *mut T::Instance);
185 let imp = instance.imp();
186
187 let supported_uri_schemes = imp.get_supported_uri_schemes();
188
189 supported_uri_schemes.as_ptr()
190}
191
192unsafe extern "C" fn parse_name<T: VfsImpl>(
193 vfs: *mut ffi::GVfs,
194 parse_name: *const c_char,
195) -> *mut ffi::GFile {
196 let instance = &*(vfs as *mut T::Instance);
197 let imp = instance.imp();
198
199 let file = imp.parse_name(&GString::from_glib_borrow(parse_name));
200
201 file.into_glib_ptr()
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
210 use crate::prelude::*;
211
212 mod imp {
214 use std::sync::LazyLock;
215
216 use super::*;
217
218 #[derive(Default)]
220 pub struct MyVfs;
221
222 #[glib::object_subclass]
223 impl ObjectSubclass for MyVfs {
224 const NAME: &'static str = "MyVfs";
225 type Type = super::MyVfs;
226 type ParentType = Vfs;
227 }
228
229 impl ObjectImpl for MyVfs {}
230
231 impl VfsImpl for MyVfs {
233 fn is_active(&self) -> bool {
234 true
235 }
236
237 fn get_file_for_path(&self, path: &std::path::Path) -> File {
238 File::for_path(path)
239 }
240
241 fn get_file_for_uri(&self, uri: &str) -> File {
242 File::for_uri(uri)
243 }
244
245 fn get_supported_uri_schemes(&self) -> &'static StrVRef {
246 static SUPPORTED_URI_SCHEMES: LazyLock<glib::StrV> =
247 LazyLock::new(|| glib::StrV::from(["file"]));
248 &SUPPORTED_URI_SCHEMES
249 }
250
251 fn parse_name(&self, parse_name: &str) -> File {
252 File::for_parse_name(parse_name)
253 }
254 }
255
256 #[derive(Default)]
258 pub struct MyCustomVfs;
259
260 #[glib::object_subclass]
261 impl ObjectSubclass for MyCustomVfs {
262 const NAME: &'static str = "MyCustomVfs";
263 type Type = super::MyCustomVfs;
264 type ParentType = super::MyVfs;
265 }
266
267 impl ObjectImpl for MyCustomVfs {}
268
269 impl VfsImpl for MyCustomVfs {}
271
272 impl MyVfsImpl for MyCustomVfs {}
273 }
274
275 glib::wrapper! {
276 pub struct MyVfs(ObjectSubclass<imp::MyVfs>) @extends Vfs;
277 }
278
279 pub trait MyVfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<MyVfs> + IsA<Vfs>> {}
280
281 unsafe impl<T: MyVfsImpl + VfsImpl> IsSubclassable<T> for MyVfs {}
283
284 glib::wrapper! {
285 pub struct MyCustomVfs(ObjectSubclass<imp::MyCustomVfs>) @extends MyVfs, Vfs;
286 }
287
288 #[test]
289 fn vfs_is_active() {
290 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
292 let active = my_custom_vfs.is_active();
293
294 let my_vfs = glib::Object::new::<MyVfs>();
296 let expected = my_vfs.is_active();
297
298 assert_eq!(active, expected);
300 }
301
302 #[test]
303 fn vfs_get_file_for_path() {
304 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
306 let file = my_custom_vfs.file_for_path("/path");
307
308 let my_vfs = glib::Object::new::<MyVfs>();
310 let expected = my_vfs.file_for_path("/path");
311
312 assert!(file.equal(&expected));
314 }
315
316 #[test]
317 fn vfs_get_file_for_uri() {
318 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
320 let file = my_custom_vfs.file_for_uri("file:///path");
321
322 let my_vfs = glib::Object::new::<MyVfs>();
324 let expected = my_vfs.file_for_uri("file:///path");
325
326 assert!(file.equal(&expected));
328 }
329
330 #[test]
331 fn vfs_get_supported_uri_schemes() {
332 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
334 let schemes = my_custom_vfs.supported_uri_schemes();
335
336 let my_vfs = glib::Object::new::<MyVfs>();
338 let expected = my_vfs.supported_uri_schemes();
339
340 assert_eq!(schemes, expected);
342 }
343
344 #[test]
345 fn vfs_parse_name() {
346 let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
348 let file = my_custom_vfs.parse_name("file:///path");
349
350 let my_vfs = glib::Object::new::<MyVfs>();
352 let expected = my_vfs.parse_name("file:///path");
353
354 assert!(file.equal(&expected));
356 }
357}