Skip to content

Commit 8a65537

Browse files
committed
Add gio::Vfs subclass
Signed-off-by: fbrouille <[email protected]>
1 parent 4480d33 commit 8a65537

File tree

2 files changed

+328
-0
lines changed

2 files changed

+328
-0
lines changed

gio/src/subclass/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod list_model;
1111
mod output_stream;
1212
mod seekable;
1313
mod socket_control_message;
14+
mod vfs;
1415

1516
pub use self::application::ArgumentList;
1617

@@ -30,5 +31,6 @@ pub mod prelude {
3031
output_stream::{OutputStreamImpl, OutputStreamImplExt},
3132
seekable::{SeekableImpl, SeekableImplExt},
3233
socket_control_message::{SocketControlMessageImpl, SocketControlMessageImplExt},
34+
vfs::{VfsImpl, VfsImplExt},
3335
};
3436
}

gio/src/subclass/vfs.rs

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use std::path::PathBuf;
4+
5+
use glib::{prelude::*, subclass::prelude::*, translate::*, GString, GStringPtr, StrV};
6+
7+
use libc::c_char;
8+
9+
use crate::{ffi, File, Vfs};
10+
11+
// Support custom implementation of virtual functions defined in `gio::ffi::GVfsClass`.
12+
pub 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) -> &[GStringPtr] {
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`.
35+
pub 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) -> &[GStringPtr] {
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+
StrV::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+
116+
impl<T: VfsImpl> VfsImplExt for T {}
117+
118+
// Implement virtual functions defined in `gio::ffi::GVfsClass`.
119+
unsafe 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+
132+
unsafe 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+
141+
unsafe 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+
153+
unsafe 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+
165+
unsafe 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() as *const *const _
174+
}
175+
176+
unsafe 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)]
189+
mod tests {
190+
// The following tests rely on a custom type `MyLocalVfs` that extends the existing GIO type `GLocalVfs`.
191+
// For each virtual method defined in class `gio::ffi::GVfsClass`, a test checks that `MyLocalVfs` and `GLocalVfs` return the same results.
192+
// Note that a `MyLocalVfs` instance is built explicitly by calling `glib::Object::builder` whereas a a `GLocalVfs` instance is created by calling `gio::auto::Vfs::local`.
193+
194+
use super::*;
195+
use crate::prelude::*;
196+
197+
// Binding of existing GIO type GLocalVfs.
198+
mod ffi {
199+
use crate::ffi;
200+
201+
#[derive(Copy, Clone)]
202+
#[repr(C)]
203+
pub struct GLocalVfs {
204+
pub parent_instance: ffi::GVfs,
205+
}
206+
207+
#[derive(Copy, Clone)]
208+
#[repr(C)]
209+
pub struct GLocalVfsClass {
210+
pub parent_class: ffi::GVfsClass,
211+
}
212+
}
213+
214+
glib::wrapper! {
215+
#[doc(alias = "GLocalVfs")]
216+
pub struct LocalVfs(Object<ffi::GLocalVfs, ffi::GLocalVfsClass>) @extends Vfs;
217+
218+
match fn {
219+
type_ => || {
220+
use std::sync::Once;
221+
static ONCE: Once = Once::new();
222+
223+
// ensure type is initialized by calling `gio::auto::File::for_path` to create a `GLocalFile` instance.
224+
ONCE.call_once(|| unsafe {
225+
let _ = File::for_path("path");
226+
});
227+
glib::gobject_ffi::g_type_from_name("GLocalVfs".to_glib_none().0)
228+
},
229+
}
230+
}
231+
232+
pub trait LocalVfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<LocalVfs> + IsA<Vfs>> {}
233+
234+
unsafe impl<T: LocalVfsImpl + VfsImpl> IsSubclassable<T> for LocalVfs {}
235+
236+
// Define `MyLocalVfs` as a subclass of `GLocalVfs`.
237+
mod imp {
238+
use super::*;
239+
240+
#[derive(Default)]
241+
pub struct MyLocalVfs;
242+
243+
#[glib::object_subclass]
244+
impl ObjectSubclass for MyLocalVfs {
245+
const NAME: &'static str = "MyLocalVfs";
246+
type Type = super::MyLocalVfs;
247+
type ParentType = LocalVfs;
248+
}
249+
250+
impl ObjectImpl for MyLocalVfs {}
251+
252+
// Implements `VfsImpl` with default implementation, which calls the parent's implementation.
253+
impl VfsImpl for MyLocalVfs {}
254+
255+
impl LocalVfsImpl for MyLocalVfs {}
256+
}
257+
258+
glib::wrapper! {
259+
pub struct MyLocalVfs(ObjectSubclass<imp::MyLocalVfs>) @extends LocalVfs, Vfs;
260+
}
261+
262+
#[test]
263+
fn vfs_is_active() {
264+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::is_active`
265+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
266+
let active = my_local_vfs.is_active();
267+
268+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::is_active`
269+
let expected = Vfs::local().is_active();
270+
271+
// both results should equal
272+
assert_eq!(active, expected);
273+
}
274+
275+
#[test]
276+
fn vfs_get_file_for_path() {
277+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
278+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
279+
let file = my_local_vfs.file_for_path("/path");
280+
281+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
282+
let expected = Vfs::local().file_for_path("/path");
283+
284+
// both files should equal
285+
assert!(file.equal(&expected));
286+
}
287+
288+
#[test]
289+
fn vfs_get_file_for_uri() {
290+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
291+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
292+
let file = my_local_vfs.file_for_uri("file:///path");
293+
294+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
295+
let expected = Vfs::local().file_for_uri("file:///path");
296+
297+
// both files should equal
298+
assert!(file.equal(&expected));
299+
}
300+
301+
#[test]
302+
fn vfs_get_supported_uri_schemes() {
303+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
304+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
305+
let schemes = my_local_vfs.supported_uri_schemes();
306+
307+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
308+
let expected = Vfs::local().supported_uri_schemes();
309+
310+
// both results should equal
311+
assert_eq!(schemes, expected);
312+
}
313+
314+
#[test]
315+
fn vfs_parse_name() {
316+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::parse_name`
317+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
318+
let file = my_local_vfs.parse_name("file:///path");
319+
320+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::parse_name`
321+
let expected = Vfs::local().parse_name("file:///path");
322+
323+
// both files should equal
324+
assert!(file.equal(&expected));
325+
}
326+
}

0 commit comments

Comments
 (0)