Skip to content

Commit 611dc0d

Browse files
authored
Merge pull request #1726 from fbrouille/gio_vfs_subclass
Add gio::Vfs subclass
2 parents efb359d + 7cf9052 commit 611dc0d

File tree

5 files changed

+564
-5
lines changed

5 files changed

+564
-5
lines changed

gio/src/subclass/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod list_model;
1212
mod output_stream;
1313
mod seekable;
1414
mod socket_control_message;
15+
mod vfs;
1516

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

@@ -32,5 +33,6 @@ pub mod prelude {
3233
output_stream::{OutputStreamImpl, OutputStreamImplExt},
3334
seekable::{SeekableImpl, SeekableImplExt},
3435
socket_control_message::{SocketControlMessageImpl, SocketControlMessageImplExt},
36+
vfs::{VfsImpl, VfsImplExt},
3537
};
3638
}

gio/src/subclass/vfs.rs

Lines changed: 341 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
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, StrVRef};
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) -> &'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+
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) -> &'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+
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()
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 `MyCustomVfs` that extends another custom type `MyVfs`.
191+
// For each virtual method defined in class `gio::ffi::GVfsClass`, a test checks that `MyCustomVfs` and `MyVfs` return the same results.
192+
193+
use super::*;
194+
use crate::prelude::*;
195+
196+
// Define `MyCustomVfs` as a subclass of `MyVfs`.
197+
mod imp {
198+
use std::sync::LazyLock;
199+
200+
use super::*;
201+
202+
// Defines `MyVfs` as a subclass of `Vfs`.
203+
#[derive(Default)]
204+
pub struct MyVfs;
205+
206+
#[glib::object_subclass]
207+
impl ObjectSubclass for MyVfs {
208+
const NAME: &'static str = "MyVfs";
209+
type Type = super::MyVfs;
210+
type ParentType = Vfs;
211+
}
212+
213+
impl ObjectImpl for MyVfs {}
214+
215+
// Implements `VfsImpl` with custom implementation.
216+
impl VfsImpl for MyVfs {
217+
fn is_active(&self) -> bool {
218+
true
219+
}
220+
221+
fn get_file_for_path(&self, path: &std::path::Path) -> File {
222+
File::for_path(path)
223+
}
224+
225+
fn get_file_for_uri(&self, uri: &str) -> File {
226+
File::for_uri(uri)
227+
}
228+
229+
fn get_supported_uri_schemes(&self) -> &'static StrVRef {
230+
static SUPPORTED_URI_SCHEMES: LazyLock<glib::StrV> =
231+
LazyLock::new(|| glib::StrV::from(["file"]));
232+
&SUPPORTED_URI_SCHEMES
233+
}
234+
235+
fn parse_name(&self, parse_name: &str) -> File {
236+
File::for_parse_name(parse_name)
237+
}
238+
}
239+
240+
// Defines `MyCustomVfs` as a subclass of `MyVfs`.
241+
#[derive(Default)]
242+
pub struct MyCustomVfs;
243+
244+
#[glib::object_subclass]
245+
impl ObjectSubclass for MyCustomVfs {
246+
const NAME: &'static str = "MyCustomVfs";
247+
type Type = super::MyCustomVfs;
248+
type ParentType = super::MyVfs;
249+
}
250+
251+
impl ObjectImpl for MyCustomVfs {}
252+
253+
// Implements `VfsImpl` with default implementation, which calls the parent's implementation.
254+
impl VfsImpl for MyCustomVfs {}
255+
256+
impl MyVfsImpl for MyCustomVfs {}
257+
}
258+
259+
glib::wrapper! {
260+
pub struct MyVfs(ObjectSubclass<imp::MyVfs>) @extends Vfs;
261+
}
262+
263+
pub trait MyVfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<MyVfs> + IsA<Vfs>> {}
264+
265+
// To make this class subclassable we need to implement IsSubclassable
266+
unsafe impl<T: MyVfsImpl + VfsImpl> IsSubclassable<T> for MyVfs {}
267+
268+
glib::wrapper! {
269+
pub struct MyCustomVfs(ObjectSubclass<imp::MyCustomVfs>) @extends MyVfs, Vfs;
270+
}
271+
272+
#[test]
273+
fn vfs_is_active() {
274+
// invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::is_active`
275+
let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
276+
let active = my_custom_vfs.is_active();
277+
278+
// invoke `MyVfs` implementation of `gio::ffi::GVfsClass::is_active`
279+
let my_vfs = glib::Object::new::<MyVfs>();
280+
let expected = my_vfs.is_active();
281+
282+
// both results should equal
283+
assert_eq!(active, expected);
284+
}
285+
286+
#[test]
287+
fn vfs_get_file_for_path() {
288+
// invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
289+
let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
290+
let file = my_custom_vfs.file_for_path("/path");
291+
292+
// invoke `MyVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
293+
let my_vfs = glib::Object::new::<MyVfs>();
294+
let expected = my_vfs.file_for_path("/path");
295+
296+
// both files should equal
297+
assert!(file.equal(&expected));
298+
}
299+
300+
#[test]
301+
fn vfs_get_file_for_uri() {
302+
// invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
303+
let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
304+
let file = my_custom_vfs.file_for_uri("file:///path");
305+
306+
// invoke `MyVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
307+
let my_vfs = glib::Object::new::<MyVfs>();
308+
let expected = my_vfs.file_for_uri("file:///path");
309+
310+
// both files should equal
311+
assert!(file.equal(&expected));
312+
}
313+
314+
#[test]
315+
fn vfs_get_supported_uri_schemes() {
316+
// invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
317+
let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
318+
let schemes = my_custom_vfs.supported_uri_schemes();
319+
320+
// invoke `MyVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
321+
let my_vfs = glib::Object::new::<MyVfs>();
322+
let expected = my_vfs.supported_uri_schemes();
323+
324+
// both results should equal
325+
assert_eq!(schemes, expected);
326+
}
327+
328+
#[test]
329+
fn vfs_parse_name() {
330+
// invoke `MyCustomVfs` implementation of `gio::ffi::GVfsClass::parse_name`
331+
let my_custom_vfs = glib::Object::new::<MyCustomVfs>();
332+
let file = my_custom_vfs.parse_name("file:///path");
333+
334+
// invoke `MyVfs` implementation of `gio::ffi::GVfsClass::parse_name`
335+
let my_vfs = glib::Object::new::<MyVfs>();
336+
let expected = my_vfs.parse_name("file:///path");
337+
338+
// both files should equal
339+
assert!(file.equal(&expected));
340+
}
341+
}

glib/src/collections/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ pub mod slist;
1313
pub use slist::SList;
1414

1515
pub mod strv;
16-
pub use strv::StrV;
16+
pub use strv::{StrV, StrVRef};

0 commit comments

Comments
 (0)