Skip to content

Commit 63b4266

Browse files
committed
Add gio::Vfs subclass
Signed-off-by: fbrouille <fbrouille@users.noreply.github.com>
1 parent c589d57 commit 63b4266

File tree

3 files changed

+324
-0
lines changed

3 files changed

+324
-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: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
3+
use glib::{prelude::*, subclass::prelude::*, translate::*, GString};
4+
5+
use libc::c_char;
6+
7+
use crate::{ffi, File, Vfs};
8+
9+
// Support custom implementation of virtual functions defined in `gio::ffi::GVfsClass`.
10+
pub trait VfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<Vfs>> {
11+
fn is_active(&self) -> bool {
12+
self.parent_is_active()
13+
}
14+
15+
fn get_file_for_path(&self, path: impl AsRef<std::path::Path>) -> File {
16+
self.parent_get_file_for_path(path)
17+
}
18+
19+
fn get_file_for_uri(&self, uri: &str) -> File {
20+
self.parent_get_file_for_uri(uri)
21+
}
22+
23+
fn get_supported_uri_schemes(&self) -> Vec<String> {
24+
self.parent_get_supported_uri_schemes()
25+
}
26+
27+
fn parse_name(&self, parse_name: &str) -> File {
28+
self.parent_parse_name(parse_name)
29+
}
30+
}
31+
32+
// Support parent implementation of virtual functions defined in `gio::ffi::GVfsClass`.
33+
pub trait VfsImplExt: VfsImpl {
34+
fn parent_is_active(&self) -> bool {
35+
unsafe {
36+
let data = Self::type_data();
37+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
38+
39+
let f = (*parent_class)
40+
.is_active
41+
.expect("No parent class implementation for \"is_active\"");
42+
43+
let res = f(self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0);
44+
from_glib(res)
45+
}
46+
}
47+
48+
fn parent_get_file_for_path(&self, path: impl AsRef<std::path::Path>) -> File {
49+
unsafe {
50+
let data = Self::type_data();
51+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
52+
53+
let f = (*parent_class)
54+
.get_file_for_path
55+
.expect("No parent class implementation for \"get_file_for_path\"");
56+
57+
let res = f(
58+
self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
59+
path.as_ref().to_glib_none().0,
60+
);
61+
from_glib_full(res)
62+
}
63+
}
64+
65+
fn parent_get_file_for_uri(&self, uri: &str) -> File {
66+
unsafe {
67+
let data = Self::type_data();
68+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
69+
70+
let f = (*parent_class)
71+
.get_file_for_uri
72+
.expect("No parent class implementation for \"get_file_for_uri\"");
73+
74+
let res = f(
75+
self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
76+
uri.to_glib_none().0,
77+
);
78+
from_glib_full(res)
79+
}
80+
}
81+
82+
fn parent_get_supported_uri_schemes(&self) -> Vec<String> {
83+
unsafe {
84+
let data = Self::type_data();
85+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
86+
87+
let f = (*parent_class)
88+
.get_supported_uri_schemes
89+
.expect("No parent class implementation for \"get_supported_uri_schemes\"");
90+
91+
let res = f(self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0);
92+
FromGlibPtrContainer::from_glib_none(res)
93+
}
94+
}
95+
96+
fn parent_parse_name(&self, parse_name: &str) -> File {
97+
unsafe {
98+
let data = Self::type_data();
99+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
100+
101+
let f = (*parent_class)
102+
.parse_name
103+
.expect("No parent class implementation for \"parse_name\"");
104+
105+
let res = f(
106+
self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
107+
parse_name.to_glib_none().0,
108+
);
109+
from_glib_full(res)
110+
}
111+
}
112+
}
113+
114+
impl<T: VfsImpl> VfsImplExt for T {}
115+
116+
// Implement virtual functions defined in `gio::ffi::GVfsClass`.
117+
unsafe impl<T: VfsImpl> IsSubclassable<T> for Vfs {
118+
fn class_init(class: &mut ::glib::Class<Self>) {
119+
Self::parent_class_init::<T>(class);
120+
121+
let klass = class.as_mut();
122+
klass.is_active = Some(is_active::<T>);
123+
klass.get_file_for_path = Some(get_file_for_path::<T>);
124+
klass.get_file_for_uri = Some(get_file_for_uri::<T>);
125+
klass.get_supported_uri_schemes = Some(get_supported_uri_schemes::<T>);
126+
klass.parse_name = Some(parse_name::<T>);
127+
}
128+
}
129+
130+
unsafe extern "C" fn is_active<T: VfsImpl>(vfs: *mut ffi::GVfs) -> glib::ffi::gboolean {
131+
let instance = &*(vfs as *mut T::Instance);
132+
let imp = instance.imp();
133+
134+
let res = imp.is_active();
135+
136+
res.into_glib()
137+
}
138+
139+
unsafe extern "C" fn get_file_for_path<T: VfsImpl>(
140+
vfs: *mut ffi::GVfs,
141+
path: *const c_char,
142+
) -> *mut ffi::GFile {
143+
let instance = &*(vfs as *mut T::Instance);
144+
let imp = instance.imp();
145+
146+
let file = imp.get_file_for_path(GString::from_glib_borrow(path).as_ref());
147+
148+
file.to_glib_full()
149+
}
150+
151+
unsafe extern "C" fn get_file_for_uri<T: VfsImpl>(
152+
vfs: *mut ffi::GVfs,
153+
uri: *const c_char,
154+
) -> *mut ffi::GFile {
155+
let instance = &*(vfs as *mut T::Instance);
156+
let imp = instance.imp();
157+
158+
let file = imp.get_file_for_uri(&GString::from_glib_borrow(uri));
159+
160+
file.to_glib_full()
161+
}
162+
163+
unsafe extern "C" fn get_supported_uri_schemes<T: VfsImpl>(
164+
vfs: *mut ffi::GVfs,
165+
) -> *const *const c_char {
166+
let instance = &*(vfs as *mut T::Instance);
167+
let imp = instance.imp();
168+
169+
let supported_uri_schemes = imp.get_supported_uri_schemes();
170+
171+
supported_uri_schemes.to_glib_full()
172+
}
173+
174+
unsafe extern "C" fn parse_name<T: VfsImpl>(
175+
vfs: *mut ffi::GVfs,
176+
parse_name: *const c_char,
177+
) -> *mut ffi::GFile {
178+
let instance = &*(vfs as *mut T::Instance);
179+
let imp = instance.imp();
180+
181+
let file = imp.parse_name(&GString::from_glib_borrow(parse_name));
182+
183+
file.to_glib_full()
184+
}

gio/tests/vfs.rs

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
// Take a look at the license at the top of the repository in the LICENSE file.
2+
//
3+
// The following tests rely on a custom type `MyLocalVfs` that extends the existing GIO type `GLocalVfs`.
4+
// For each virtual method defined in class `gio::ffi::GVfsClass`, a test checks that `MyLocalVfs` and `GLocalVfs` return the same results.
5+
// 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`.
6+
7+
use gio::{prelude::*, subclass::prelude::*, File, Vfs};
8+
use glib::translate::ToGlibPtr;
9+
10+
// Binding of existing GIO type GLocalVfs.
11+
mod ffi {
12+
use gio::ffi;
13+
14+
#[derive(Copy, Clone)]
15+
#[repr(C)]
16+
pub struct GLocalVfs {
17+
pub parent_instance: ffi::GVfs,
18+
}
19+
20+
#[derive(Copy, Clone)]
21+
#[repr(C)]
22+
pub struct GLocalVfsClass {
23+
pub parent_class: ffi::GVfsClass,
24+
}
25+
}
26+
27+
glib::wrapper! {
28+
#[doc(alias = "GLocalVfs")]
29+
pub struct LocalVfs(Object<ffi::GLocalVfs, ffi::GLocalVfsClass>) @extends Vfs;
30+
31+
match fn {
32+
type_ => || {
33+
use std::sync::Once;
34+
static ONCE: Once = Once::new();
35+
36+
// ensure type is initialized by calling `gio::auto::File::for_path` to create a `GLocalFile` instance.
37+
ONCE.call_once(|| unsafe {
38+
let _ = File::for_path("path");
39+
});
40+
glib::gobject_ffi::g_type_from_name("GLocalVfs".to_glib_none().0)
41+
},
42+
}
43+
}
44+
45+
pub trait LocalVfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<LocalVfs> + IsA<Vfs>> {}
46+
47+
unsafe impl<T: LocalVfsImpl + VfsImpl> IsSubclassable<T> for LocalVfs {}
48+
49+
// Define `MyLocalVfs` as a subclass of `GLocalVfs`.
50+
mod imp {
51+
use super::*;
52+
53+
#[derive(Default)]
54+
pub struct MyLocalVfs;
55+
56+
#[glib::object_subclass]
57+
impl ObjectSubclass for MyLocalVfs {
58+
const NAME: &'static str = "MyLocalVfs";
59+
type Type = super::MyLocalVfs;
60+
type ParentType = LocalVfs;
61+
}
62+
63+
impl ObjectImpl for MyLocalVfs {}
64+
65+
// Implements `VfsImpl` with default implementation, which calls the parent's implementation.
66+
impl VfsImpl for MyLocalVfs {}
67+
68+
impl LocalVfsImpl for MyLocalVfs {}
69+
}
70+
71+
glib::wrapper! {
72+
pub struct MyLocalVfs(ObjectSubclass<imp::MyLocalVfs>) @extends LocalVfs, Vfs;
73+
}
74+
75+
#[test]
76+
fn vfs_is_active() {
77+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::is_active`
78+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
79+
let active = my_local_vfs.is_active();
80+
81+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::is_active`
82+
let expected = Vfs::local().is_active();
83+
84+
// both results should equal
85+
assert_eq!(active, expected);
86+
}
87+
88+
#[test]
89+
fn vfs_get_file_for_path() {
90+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
91+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
92+
let file = my_local_vfs.file_for_path("/path");
93+
94+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
95+
let expected = Vfs::local().file_for_path("/path");
96+
97+
// both files should equal
98+
assert!(file.equal(&expected));
99+
}
100+
101+
#[test]
102+
fn vfs_get_file_for_uri() {
103+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
104+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
105+
let file = my_local_vfs.file_for_uri("file:///path");
106+
107+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
108+
let expected = Vfs::local().file_for_uri("file:///path");
109+
110+
// both files should equal
111+
assert!(file.equal(&expected));
112+
}
113+
114+
#[test]
115+
fn vfs_get_supported_uri_schemes() {
116+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
117+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
118+
let schemes = my_local_vfs.supported_uri_schemes();
119+
120+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
121+
let expected = Vfs::local().supported_uri_schemes();
122+
123+
// both results should equal
124+
assert_eq!(schemes, expected);
125+
}
126+
127+
#[test]
128+
fn vfs_parse_name() {
129+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::parse_name`
130+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
131+
let file = my_local_vfs.parse_name("file:///path");
132+
133+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::parse_name`
134+
let expected = Vfs::local().parse_name("file:///path");
135+
136+
// both files should equal
137+
assert!(file.equal(&expected));
138+
}

0 commit comments

Comments
 (0)