use crate::DataSource;
use gio::Cancellable;
use glib::subclass::prelude::*;
use glib::translate::*;
use glib::{Cast, IsA};
use std::{future::Future, pin::Pin};
pub trait DataSourceImpl: ObjectImpl {
fn tile_data_future(
&self,
x: i32,
y: i32,
zoom_level: i32,
) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>>;
}
pub trait DataSourceImplExt: ObjectSubclass {
fn parent_tile_data_async<
Q: IsA<Cancellable>,
C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
>(
&self,
x: i32,
y: i32,
zoom_level: i32,
cancellable: Option<&Q>,
callback: C,
);
fn parent_tile_data_future(
&self,
x: i32,
y: i32,
zoom_level: i32,
) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>>;
}
impl<T: DataSourceImpl> DataSourceImplExt for T {
#[allow(clippy::type_complexity)]
fn parent_tile_data_async<
Q: IsA<Cancellable>,
C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
>(
&self,
x: i32,
y: i32,
zoom_level: i32,
cancellable: Option<&Q>,
callback: C,
) {
unsafe {
let data = T::type_data();
let parent_class = data.as_ref().parent_class() as *mut ffi::ShumateDataSourceClass;
let f = (*parent_class)
.get_tile_data_async
.expect("no parent \"get_tile_data_async\" implementation");
let finish = (*parent_class)
.get_tile_data_finish
.expect("no parent \"get_tile_data_finish\" implementation");
let user_data: Box<(C, _)> = Box::new((callback, finish));
unsafe extern "C" fn parent_get_tile_data_async_trampoline<
C: FnOnce(Result<glib::Bytes, glib::Error>) + Send + 'static,
>(
source_object_ptr: *mut glib::gobject_ffi::GObject,
res: *mut gio::ffi::GAsyncResult,
user_data: glib::ffi::gpointer,
) {
let mut error = std::ptr::null_mut();
let cb: Box<(
C,
fn(
*mut ffi::ShumateDataSource,
*mut gio::ffi::GAsyncResult,
*mut *mut glib::ffi::GError,
) -> *mut glib::ffi::GBytes,
)> = Box::from_raw(user_data as *mut _);
let bytes = cb.1(source_object_ptr as _, res, &mut error);
let result = if error.is_null() {
Ok(from_glib_full(bytes))
} else {
Err(from_glib_full(error))
};
cb.0(result);
}
let cancellable = cancellable.map(|p| p.as_ref());
let callback = parent_get_tile_data_async_trampoline::<C>;
f(
self.instance()
.unsafe_cast_ref::<DataSource>()
.to_glib_none()
.0,
x,
y,
zoom_level,
cancellable.to_glib_none().0,
Some(callback),
Box::into_raw(user_data) as *mut _,
);
}
}
fn parent_tile_data_future(
&self,
x: i32,
y: i32,
zoom_level: i32,
) -> Pin<Box<dyn Future<Output = Result<glib::Bytes, glib::Error>> + 'static>> {
Box::pin(gio::GioFuture::new(
&self.ref_counted(),
move |imp, cancellable, send| {
imp.parent_tile_data_async(x, y, zoom_level, Some(cancellable), move |res| {
send.resolve(res);
});
},
))
}
}
unsafe impl<T: DataSourceImpl> IsSubclassable<T> for DataSource {
fn class_init(class: &mut glib::Class<Self>) {
Self::parent_class_init::<T>(class);
let klass = class.as_mut();
klass.get_tile_data_async = Some(data_source_get_tile_data_async::<T>);
klass.get_tile_data_finish = Some(data_source_get_tile_data_finish::<T>);
}
}
unsafe extern "C" fn data_source_get_tile_data_async<T: DataSourceImpl>(
ptr: *mut ffi::ShumateDataSource,
x: i32,
y: i32,
zoom_level: i32,
cancellable_ptr: *mut gio::ffi::GCancellable,
callback: gio::ffi::GAsyncReadyCallback,
user_data: glib::ffi::gpointer,
) {
let instance = &*(ptr as *mut T::Instance);
let imp = instance.imp();
let cancellable: Option<gio::Cancellable> = from_glib_none(cancellable_ptr);
let closure = move |result: gio::LocalTask<glib::Bytes>, source_object: Option<&DataSource>| {
let result: *mut gio::ffi::GAsyncResult = result
.unsafe_cast_ref::<gio::AsyncResult>()
.to_glib_none()
.0;
let source_object = source_object
.map(|o| o.unsafe_cast_ref::<glib::Object>())
.to_glib_none()
.0;
callback.unwrap()(source_object, result, user_data)
};
let t = gio::LocalTask::new(None, cancellable.as_ref(), closure);
glib::MainContext::default().spawn_local(async move {
let res = imp.tile_data_future(x, y, zoom_level).await;
t.return_result(res);
});
}
unsafe extern "C" fn data_source_get_tile_data_finish<T: DataSourceImpl>(
_ptr: *mut ffi::ShumateDataSource,
res_ptr: *mut gio::ffi::GAsyncResult,
error_ptr: *mut *mut glib::ffi::GError,
) -> *mut glib::ffi::GBytes {
let res: gio::AsyncResult = from_glib_none(res_ptr);
let task = res.downcast::<gio::LocalTask<glib::Bytes>>().unwrap();
match task.propagate() {
Ok(bytes) => bytes.to_glib_full(),
Err(e) => {
*error_ptr = e.into_glib_ptr();
std::ptr::null_mut()
}
}
}