Skip to content

Commit b831e69

Browse files
committed
Add gio::Vfs subclass
Signed-off-by: fbrouille <[email protected]>
1 parent 63875ab commit b831e69

File tree

2 files changed

+339
-0
lines changed

2 files changed

+339
-0
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: 337 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,337 @@
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+
// rustdoc-stripper-ignore-next
26+
/// Gets a list of URI schemes supported by vfs.
27+
///
28+
/// # Safety
29+
///
30+
/// Implementation has to ensure it returns a `&'static [GStringPtr]` that is a `NULL`-terminated C array,
31+
/// e.g. by building a `static LazyLock<StrV>` that derefs into `&'static [glib::GStringPtr]`.
32+
unsafe fn get_supported_uri_schemes(&self) -> &'static [GStringPtr] {
33+
self.parent_get_supported_uri_schemes()
34+
}
35+
36+
fn parse_name(&self, parse_name: &str) -> File {
37+
self.parent_parse_name(parse_name)
38+
}
39+
}
40+
41+
// Support parent implementation of virtual functions defined in `gio::ffi::GVfsClass`.
42+
pub trait VfsImplExt: VfsImpl {
43+
fn parent_is_active(&self) -> bool {
44+
unsafe {
45+
let data = Self::type_data();
46+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
47+
48+
let f = (*parent_class)
49+
.is_active
50+
.expect("No parent class implementation for \"is_active\"");
51+
52+
let res = f(self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0);
53+
from_glib(res)
54+
}
55+
}
56+
57+
fn parent_get_file_for_path(&self, path: &std::path::Path) -> File {
58+
unsafe {
59+
let data = Self::type_data();
60+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
61+
62+
let f = (*parent_class)
63+
.get_file_for_path
64+
.expect("No parent class implementation for \"get_file_for_path\"");
65+
66+
let res = f(
67+
self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
68+
path.to_glib_none().0,
69+
);
70+
from_glib_full(res)
71+
}
72+
}
73+
74+
fn parent_get_file_for_uri(&self, uri: &str) -> File {
75+
unsafe {
76+
let data = Self::type_data();
77+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
78+
79+
let f = (*parent_class)
80+
.get_file_for_uri
81+
.expect("No parent class implementation for \"get_file_for_uri\"");
82+
83+
let res = f(
84+
self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
85+
uri.to_glib_none().0,
86+
);
87+
from_glib_full(res)
88+
}
89+
}
90+
91+
fn parent_get_supported_uri_schemes(&self) -> &'static [GStringPtr] {
92+
unsafe {
93+
let data = Self::type_data();
94+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
95+
96+
let f = (*parent_class)
97+
.get_supported_uri_schemes
98+
.expect("No parent class implementation for \"get_supported_uri_schemes\"");
99+
100+
let res = f(self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0);
101+
StrV::from_glib_borrow(res)
102+
}
103+
}
104+
105+
fn parent_parse_name(&self, parse_name: &str) -> File {
106+
unsafe {
107+
let data = Self::type_data();
108+
let parent_class = data.as_ref().parent_class() as *const ffi::GVfsClass;
109+
110+
let f = (*parent_class)
111+
.parse_name
112+
.expect("No parent class implementation for \"parse_name\"");
113+
114+
let res = f(
115+
self.obj().unsafe_cast_ref::<Vfs>().to_glib_none().0,
116+
parse_name.to_glib_none().0,
117+
);
118+
from_glib_full(res)
119+
}
120+
}
121+
}
122+
123+
impl<T: VfsImpl> VfsImplExt for T {}
124+
125+
// Implement virtual functions defined in `gio::ffi::GVfsClass`.
126+
unsafe impl<T: VfsImpl> IsSubclassable<T> for Vfs {
127+
fn class_init(class: &mut ::glib::Class<Self>) {
128+
Self::parent_class_init::<T>(class);
129+
130+
let klass = class.as_mut();
131+
klass.is_active = Some(is_active::<T>);
132+
klass.get_file_for_path = Some(get_file_for_path::<T>);
133+
klass.get_file_for_uri = Some(get_file_for_uri::<T>);
134+
klass.get_supported_uri_schemes = Some(get_supported_uri_schemes::<T>);
135+
klass.parse_name = Some(parse_name::<T>);
136+
}
137+
}
138+
139+
unsafe extern "C" fn is_active<T: VfsImpl>(vfs: *mut ffi::GVfs) -> glib::ffi::gboolean {
140+
let instance = &*(vfs as *mut T::Instance);
141+
let imp = instance.imp();
142+
143+
let res = imp.is_active();
144+
145+
res.into_glib()
146+
}
147+
148+
unsafe extern "C" fn get_file_for_path<T: VfsImpl>(
149+
vfs: *mut ffi::GVfs,
150+
path: *const c_char,
151+
) -> *mut ffi::GFile {
152+
let instance = &*(vfs as *mut T::Instance);
153+
let imp = instance.imp();
154+
155+
let file = imp.get_file_for_path(&PathBuf::from_glib_none(path));
156+
157+
file.into_glib_ptr()
158+
}
159+
160+
unsafe extern "C" fn get_file_for_uri<T: VfsImpl>(
161+
vfs: *mut ffi::GVfs,
162+
uri: *const c_char,
163+
) -> *mut ffi::GFile {
164+
let instance = &*(vfs as *mut T::Instance);
165+
let imp = instance.imp();
166+
167+
let file = imp.get_file_for_uri(&GString::from_glib_borrow(uri));
168+
169+
file.into_glib_ptr()
170+
}
171+
172+
unsafe extern "C" fn get_supported_uri_schemes<T: VfsImpl>(
173+
vfs: *mut ffi::GVfs,
174+
) -> *const *const c_char {
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+
let ptr = supported_uri_schemes.as_ptr() as *const *const c_char;
180+
181+
// Verify ptr is a `NULL`-terminated C array.
182+
assert!((*ptr.add(supported_uri_schemes.len())).is_null(), "get_supported_uri_schemes must return a &'static [GStringPtr] that is a `NULL`-terminated C array");
183+
184+
ptr
185+
}
186+
187+
unsafe extern "C" fn parse_name<T: VfsImpl>(
188+
vfs: *mut ffi::GVfs,
189+
parse_name: *const c_char,
190+
) -> *mut ffi::GFile {
191+
let instance = &*(vfs as *mut T::Instance);
192+
let imp = instance.imp();
193+
194+
let file = imp.parse_name(&GString::from_glib_borrow(parse_name));
195+
196+
file.into_glib_ptr()
197+
}
198+
199+
#[cfg(test)]
200+
mod tests {
201+
// The following tests rely on a custom type `MyLocalVfs` that extends the existing GIO type `GLocalVfs`.
202+
// For each virtual method defined in class `gio::ffi::GVfsClass`, a test checks that `MyLocalVfs` and `GLocalVfs` return the same results.
203+
// 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`.
204+
205+
use super::*;
206+
use crate::prelude::*;
207+
208+
// Binding of existing GIO type GLocalVfs.
209+
mod ffi {
210+
use crate::ffi;
211+
212+
#[derive(Copy, Clone)]
213+
#[repr(C)]
214+
pub struct GLocalVfs {
215+
pub parent_instance: ffi::GVfs,
216+
}
217+
218+
#[derive(Copy, Clone)]
219+
#[repr(C)]
220+
pub struct GLocalVfsClass {
221+
pub parent_class: ffi::GVfsClass,
222+
}
223+
}
224+
225+
glib::wrapper! {
226+
#[doc(alias = "GLocalVfs")]
227+
pub struct LocalVfs(Object<ffi::GLocalVfs, ffi::GLocalVfsClass>) @extends Vfs;
228+
229+
match fn {
230+
type_ => || {
231+
use std::sync::Once;
232+
static ONCE: Once = Once::new();
233+
234+
// ensure type is initialized by calling `gio::auto::File::for_path` to create a `GLocalFile` instance.
235+
ONCE.call_once(|| unsafe {
236+
let _ = File::for_path("path");
237+
});
238+
glib::gobject_ffi::g_type_from_name("GLocalVfs".to_glib_none().0)
239+
},
240+
}
241+
}
242+
243+
pub trait LocalVfsImpl: ObjectImpl + ObjectSubclass<Type: IsA<LocalVfs> + IsA<Vfs>> {}
244+
245+
unsafe impl<T: LocalVfsImpl + VfsImpl> IsSubclassable<T> for LocalVfs {}
246+
247+
// Define `MyLocalVfs` as a subclass of `GLocalVfs`.
248+
mod imp {
249+
use super::*;
250+
251+
#[derive(Default)]
252+
pub struct MyLocalVfs;
253+
254+
#[glib::object_subclass]
255+
impl ObjectSubclass for MyLocalVfs {
256+
const NAME: &'static str = "MyLocalVfs";
257+
type Type = super::MyLocalVfs;
258+
type ParentType = LocalVfs;
259+
}
260+
261+
impl ObjectImpl for MyLocalVfs {}
262+
263+
// Implements `VfsImpl` with default implementation, which calls the parent's implementation.
264+
impl VfsImpl for MyLocalVfs {}
265+
266+
impl LocalVfsImpl for MyLocalVfs {}
267+
}
268+
269+
glib::wrapper! {
270+
pub struct MyLocalVfs(ObjectSubclass<imp::MyLocalVfs>) @extends LocalVfs, Vfs;
271+
}
272+
273+
#[test]
274+
fn vfs_is_active() {
275+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::is_active`
276+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
277+
let active = my_local_vfs.is_active();
278+
279+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::is_active`
280+
let expected = Vfs::local().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 `MyLocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
289+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
290+
let file = my_local_vfs.file_for_path("/path");
291+
292+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_path`
293+
let expected = Vfs::local().file_for_path("/path");
294+
295+
// both files should equal
296+
assert!(file.equal(&expected));
297+
}
298+
299+
#[test]
300+
fn vfs_get_file_for_uri() {
301+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
302+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
303+
let file = my_local_vfs.file_for_uri("file:///path");
304+
305+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::get_file_for_uri`
306+
let expected = Vfs::local().file_for_uri("file:///path");
307+
308+
// both files should equal
309+
assert!(file.equal(&expected));
310+
}
311+
312+
#[test]
313+
fn vfs_get_supported_uri_schemes() {
314+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
315+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
316+
let schemes = my_local_vfs.supported_uri_schemes();
317+
318+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::supported_uri_schemes`
319+
let expected = Vfs::local().supported_uri_schemes();
320+
321+
// both results should equal
322+
assert_eq!(schemes, expected);
323+
}
324+
325+
#[test]
326+
fn vfs_parse_name() {
327+
// invoke `MyLocalVfs` implementation of `gio::ffi::GVfsClass::parse_name`
328+
let my_local_vfs = glib::Object::new::<MyLocalVfs>();
329+
let file = my_local_vfs.parse_name("file:///path");
330+
331+
// invoke `LocalVfs` implementation of `gio::ffi::GVfsClass::parse_name`
332+
let expected = Vfs::local().parse_name("file:///path");
333+
334+
// both files should equal
335+
assert!(file.equal(&expected));
336+
}
337+
}

0 commit comments

Comments
 (0)