1#[cfg(feature = "v1_1")]
7#[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
8use crate::DataSourceRequest;
9use crate::{DataSource, prelude::*};
10use gio::Cancellable;
11use glib::subclass::prelude::*;
12use glib::translate::*;
13use std::{future::Future, pin::Pin};
14
15pub trait DataSourceImpl: ObjectImpl {
16 fn tile_data_future(
17 &self,
18 x: i32,
19 y: i32,
20 zoom_level: i32,
21 ) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>>;
22
23 #[cfg(feature = "v1_1")]
38 #[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
39 fn start_request(
40 &self,
41 x: i32,
42 y: i32,
43 zoom_level: i32,
44 cancellable: Option<gio::Cancellable>,
45 ) -> Option<DataSourceRequest> {
46 self.parent_start_request(x, y, zoom_level, cancellable)
47 }
48}
49
50pub trait DataSourceImplExt: ObjectSubclass {
51 #[allow(clippy::type_complexity)]
52 fn parent_tile_data_async<
53 Q: IsA<Cancellable>,
54 C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
55 >(
56 &self,
57 x: i32,
58 y: i32,
59 zoom_level: i32,
60 cancellable: Option<&Q>,
61 callback: C,
62 ) {
63 unsafe {
64 let data = Self::type_data();
65 let parent_class = data.as_ref().parent_class() as *mut ffi::ShumateDataSourceClass;
66 let f = (*parent_class)
67 .get_tile_data_async
68 .expect("no parent \"get_tile_data_async\" implementation");
69 let finish = (*parent_class)
70 .get_tile_data_finish
71 .expect("no parent \"get_tile_data_finish\" implementation");
72
73 let user_data: Box<(C, _)> = Box::new((callback, finish));
74
75 unsafe extern "C" fn parent_get_tile_data_async_trampoline<
76 C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
77 >(
78 source_object_ptr: *mut glib::gobject_ffi::GObject,
79 res: *mut gio::ffi::GAsyncResult,
80 user_data: glib::ffi::gpointer,
81 ) {
82 unsafe {
83 let mut error = std::ptr::null_mut();
84 let cb: Box<(
85 C,
86 fn(
87 *mut ffi::ShumateDataSource,
88 *mut gio::ffi::GAsyncResult,
89 *mut *mut glib::ffi::GError,
90 ) -> *mut glib::ffi::GBytes,
91 )> = Box::from_raw(user_data as *mut _);
92 let bytes = cb.1(source_object_ptr as _, res, &mut error);
93 let result = if error.is_null() {
94 Ok(from_glib_full(bytes))
95 } else {
96 Err(from_glib_full(error))
97 };
98 cb.0(result);
99 }
100 }
101
102 let cancellable = cancellable.map(|p| p.as_ref());
103 let callback = parent_get_tile_data_async_trampoline::<C>;
104 f(
105 self.obj().unsafe_cast_ref::<DataSource>().to_glib_none().0,
106 x,
107 y,
108 zoom_level,
109 cancellable.to_glib_none().0,
110 Some(callback),
111 Box::into_raw(user_data) as *mut _,
112 );
113 }
114 }
115
116 fn parent_tile_data_future(
117 &self,
118 x: i32,
119 y: i32,
120 zoom_level: i32,
121 ) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>> {
122 Box::pin(gio::GioFuture::new(
123 &self.ref_counted(),
124 move |imp, cancellable, send| {
125 imp.parent_tile_data_async(x, y, zoom_level, Some(cancellable), move |res| {
126 send.resolve(res);
127 });
128 },
129 ))
130 }
131
132 #[cfg(feature = "v1_1")]
133 #[cfg_attr(docsrs, doc(cfg(feature = "v1_1")))]
134 fn parent_start_request(
135 &self,
136 x: i32,
137 y: i32,
138 zoom_level: i32,
139 cancellable: Option<gio::Cancellable>,
140 ) -> Option<DataSourceRequest> {
141 unsafe {
142 let data = Self::type_data();
143 let parent_class = data.as_ref().parent_class() as *mut ffi::ShumateDataSourceClass;
144 if let Some(f) = (*parent_class).start_request {
145 from_glib_full(f(
146 self.obj().unsafe_cast_ref::<DataSource>().to_glib_none().0,
147 x,
148 y,
149 zoom_level,
150 cancellable.to_glib_none().0,
151 ))
152 } else {
153 None
154 }
155 }
156 }
157}
158
159impl<T: DataSourceImpl> DataSourceImplExt for T {}
160
161unsafe impl<T: DataSourceImpl> IsSubclassable<T> for DataSource {
162 fn class_init(class: &mut glib::Class<Self>) {
163 Self::parent_class_init::<T>(class);
164
165 let klass = class.as_mut();
166 klass.get_tile_data_async = Some(data_source_get_tile_data_async::<T>);
167 klass.get_tile_data_finish = Some(data_source_get_tile_data_finish);
168 #[cfg(feature = "v1_1")]
169 {
170 klass.start_request = Some(data_source_start_request::<T>);
171 }
172 }
173}
174
175unsafe extern "C" fn data_source_get_tile_data_async<T: DataSourceImpl>(
176 ptr: *mut ffi::ShumateDataSource,
177 x: i32,
178 y: i32,
179 zoom_level: i32,
180 cancellable_ptr: *mut gio::ffi::GCancellable,
181 callback: gio::ffi::GAsyncReadyCallback,
182 user_data: glib::ffi::gpointer,
183) {
184 unsafe {
185 let instance = &*(ptr as *mut T::Instance);
186 let imp = instance.imp();
187 let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable_ptr);
188
189 let closure = move |result: gio::LocalTask<glib::Bytes>,
190 source_object: Option<&DataSource>| {
191 let result: *mut gio::ffi::GAsyncResult = result
192 .unsafe_cast_ref::<gio::AsyncResult>()
193 .to_glib_none()
194 .0;
195 let source_object = source_object
196 .map(|o| o.unsafe_cast_ref::<glib::Object>())
197 .to_glib_none()
198 .0;
199 callback.unwrap()(source_object, result, user_data)
200 };
201
202 let t = gio::LocalTask::new(None, cancellable.as_ref(), closure);
203
204 glib::MainContext::default().spawn_local(async move {
205 let res = imp.tile_data_future(x, y, zoom_level).await;
206 t.return_result(res);
207 });
208 }
209}
210
211unsafe extern "C" fn data_source_get_tile_data_finish(
212 _ptr: *mut ffi::ShumateDataSource,
213 res_ptr: *mut gio::ffi::GAsyncResult,
214 error_ptr: *mut *mut glib::ffi::GError,
215) -> *mut glib::ffi::GBytes {
216 unsafe {
217 let res: gio::AsyncResult = from_glib_none(res_ptr);
218 let task = res.downcast::<gio::LocalTask<glib::Bytes>>().unwrap();
219 match task.propagate() {
220 Ok(bytes) => bytes.to_glib_full(),
221 Err(e) => {
222 *error_ptr = e.into_glib_ptr();
223 std::ptr::null_mut()
224 }
225 }
226 }
227}
228
229#[cfg(feature = "v1_1")]
230unsafe extern "C" fn data_source_start_request<T: DataSourceImpl>(
231 ptr: *mut ffi::ShumateDataSource,
232 x: i32,
233 y: i32,
234 zoom_level: i32,
235 cancellable_ptr: *mut gio::ffi::GCancellable,
236) -> *mut ffi::ShumateDataSourceRequest {
237 unsafe {
238 let instance = &*(ptr as *mut T::Instance);
239 let imp = instance.imp();
240
241 imp.start_request(x, y, zoom_level, from_glib_none(cancellable_ptr))
242 .to_glib_full()
243 }
244}