Skip to content

Commit cb13964

Browse files
committed
Add gio::FileMonitor subclass
Signed-off-by: fbrouille <fbrouille@users.noreply.github.com>
1 parent 20e5522 commit cb13964

File tree

2 files changed

+166
-0
lines changed

2 files changed

+166
-0
lines changed

gio/src/subclass/file_monitor.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
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::*};
4+
5+
use crate::{ffi, FileMonitor};
6+
7+
pub trait FileMonitorImpl: ObjectImpl + ObjectSubclass<Type: IsA<FileMonitor>> {
8+
fn cancel(&self) -> bool {
9+
self.parent_cancel()
10+
}
11+
}
12+
13+
pub trait FileMonitorImplExt: FileMonitorImpl {
14+
fn parent_cancel(&self) -> bool {
15+
unsafe {
16+
let data = Self::type_data();
17+
let parent_class = data.as_ref().parent_class() as *const ffi::GFileMonitorClass;
18+
19+
let f = (*parent_class)
20+
.cancel
21+
.expect("No parent class implementation for \"cancel\"");
22+
23+
let res = f(self.obj().unsafe_cast_ref::<FileMonitor>().to_glib_none().0);
24+
from_glib(res)
25+
}
26+
}
27+
}
28+
29+
impl<T: FileMonitorImpl> FileMonitorImplExt for T {}
30+
31+
unsafe impl<T: FileMonitorImpl> IsSubclassable<T> for FileMonitor {
32+
fn class_init(class: &mut ::glib::Class<Self>) {
33+
Self::parent_class_init::<T>(class);
34+
35+
let klass = class.as_mut();
36+
klass.cancel = Some(cancel::<T>);
37+
}
38+
}
39+
40+
unsafe extern "C" fn cancel<T: FileMonitorImpl>(
41+
monitor: *mut ffi::GFileMonitor,
42+
) -> glib::ffi::gboolean {
43+
let instance = &*(monitor as *mut T::Instance);
44+
let imp = instance.imp();
45+
46+
let res = imp.cancel();
47+
48+
res.into_glib()
49+
}
50+
51+
#[cfg(test)]
52+
mod tests {
53+
use std::cell::RefCell;
54+
55+
use super::*;
56+
use crate::{prelude::*, File, FileMonitorEvent};
57+
58+
mod imp {
59+
use super::*;
60+
61+
#[derive(Default)]
62+
pub struct MyFileMonitor {
63+
pub index: RefCell<i8>,
64+
}
65+
66+
#[glib::object_subclass]
67+
impl ObjectSubclass for MyFileMonitor {
68+
const NAME: &'static str = "MyFileMonitor";
69+
type Type = super::MyFileMonitor;
70+
type ParentType = FileMonitor;
71+
}
72+
73+
impl ObjectImpl for MyFileMonitor {}
74+
75+
impl FileMonitorImpl for MyFileMonitor {
76+
fn cancel(&self) -> bool {
77+
self.index.replace(-1);
78+
true
79+
}
80+
}
81+
}
82+
83+
glib::wrapper! {
84+
pub struct MyFileMonitor(ObjectSubclass<imp::MyFileMonitor>) @extends FileMonitor;
85+
}
86+
87+
impl MyFileMonitor {
88+
pub async fn tick(&self) {
89+
std::thread::sleep(std::time::Duration::from_millis(10));
90+
let mut i = *(self.imp().index.borrow());
91+
while i != -1 {
92+
let (child, other_file, event_type) = match i % 3 {
93+
0 => (
94+
File::for_parse_name(&format!("file{}", i)),
95+
None,
96+
FileMonitorEvent::Created,
97+
),
98+
1 => (
99+
File::for_parse_name(&format!("file{}", i - 1)),
100+
Some(File::for_parse_name(&format!("file{}", i))),
101+
FileMonitorEvent::Renamed,
102+
),
103+
2 => (
104+
File::for_parse_name(&format!("file{}", i)),
105+
None,
106+
FileMonitorEvent::Deleted,
107+
),
108+
_ => unimplemented!("cannot occur"),
109+
};
110+
self.emit_event(&child, other_file.as_ref(), event_type);
111+
std::thread::sleep(std::time::Duration::from_millis(10));
112+
i = *(self.imp().index.borrow());
113+
if i != -1 {
114+
i += 1;
115+
self.imp().index.replace(i);
116+
}
117+
}
118+
}
119+
}
120+
121+
#[test]
122+
fn test_cancel() {
123+
let file_monitor = glib::Object::new::<MyFileMonitor>();
124+
let index = RefCell::new(0i8);
125+
file_monitor.connect_changed(
126+
move |self_: &MyFileMonitor,
127+
child: &File,
128+
other_file: Option<&File>,
129+
event_type: FileMonitorEvent| {
130+
let i = *(index.borrow());
131+
let (expected_child, expected_other_file, expected_event_type) = match i % 3 {
132+
0 => (
133+
File::for_parse_name(&format!("file{}", i)),
134+
None,
135+
FileMonitorEvent::Created,
136+
),
137+
1 => (
138+
File::for_parse_name(&format!("file{}", i - 1)),
139+
Some(File::for_parse_name(&format!("file{}", i))),
140+
FileMonitorEvent::Renamed,
141+
),
142+
2 => (
143+
File::for_parse_name(&format!("file{}", i)),
144+
None,
145+
FileMonitorEvent::Deleted,
146+
),
147+
_ => unimplemented!("cannot occur"),
148+
};
149+
assert_eq!(child.path(), expected_child.path());
150+
assert_eq!(
151+
other_file.and_then(FileExt::path),
152+
expected_other_file.as_ref().and_then(FileExt::path)
153+
);
154+
assert_eq!(event_type, expected_event_type);
155+
index.replace(i + 1);
156+
if i >= 8 {
157+
self_.cancel();
158+
}
159+
},
160+
);
161+
162+
glib::MainContext::new().block_on(file_monitor.tick())
163+
}
164+
}

gio/src/subclass/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ mod action_group;
44
mod action_map;
55
mod application;
66
mod async_initable;
7+
mod file_monitor;
78
mod initable;
89
mod input_stream;
910
mod io_stream;
@@ -23,6 +24,7 @@ pub mod prelude {
2324
action_map::{ActionMapImpl, ActionMapImplExt},
2425
application::{ApplicationImpl, ApplicationImplExt},
2526
async_initable::{AsyncInitableImpl, AsyncInitableImplExt},
27+
file_monitor::{FileMonitorImpl, FileMonitorImplExt},
2628
initable::{InitableImpl, InitableImplExt},
2729
input_stream::{InputStreamImpl, InputStreamImplExt},
2830
io_stream::{IOStreamImpl, IOStreamImplExt},

0 commit comments

Comments
 (0)