use std::mem::MaybeUninit;
use std::ptr::{self, NonNull, addr_of_mut};
use std::rc::{Rc, Weak};
use std::cell::{BorrowError, BorrowMutError, Cell, Ref, RefCell, RefMut};
#[macro_export]
macro_rules! call_rust_trait_impl {
(mut $self:expr, $method:ident ( $($arg:expr),* )) => {
$self.rust_obj
.try_with_borrow_mut(|vtable| {
vtable.$method($($arg),*)
})
.expect(concat!(
"Failed to borrow mutably for ",
stringify!($method),
"()"
))
};
($self:expr, $method:ident ( $($arg:expr),* )) => {
$self.rust_obj
.try_with_borrow(|vtable| {
vtable.$method($($arg),*)
})
.expect(concat!(
"Failed to borrow for ",
stringify!($method),
"()"
))
};
}
#[macro_export]
macro_rules! call_cpp_impl {
(mut $self:expr, $method:ident ( $($arg:expr),* )) => {{
let proxy = unsafe {
$self.cpp_proxy
.as_mut()
.expect("cpp_proxy was null")
};
let proxy_pinned = unsafe { std::pin::Pin::new_unchecked(proxy) };
$self.rust_obj
.try_store_handle_and_call_qml_mut(|_| proxy_pinned.$method($($arg),*))
.expect(concat!(
"Failed to borrow mutably for ",
stringify!($method),
"()"
))
}};
($self:expr, $method:ident ( $($arg:expr),* )) => {{
let proxy = unsafe {
$self.cpp_proxy
.as_ref()
.expect("cpp_proxy was null")
};
$self.rust_obj
.try_store_handle_and_call_qml(|_| proxy.$method($($arg),*))
.expect(concat!(
"Failed to borrow for ",
stringify!($method),
"()"
))
}};
}
pub struct RustObjAccess<T: ?Sized + 'static>
{
shared_reference: SharedReferenceWithQml<T>,
borrow_handle: Cell<*mut RustObjBorrowHandle<'static, T>>,
}
impl<T: ?Sized> RustObjAccess<T> {
pub fn new_strong(ptr: Rc<RefCell<T>>) -> Self {
Self {
shared_reference: SharedReferenceWithQml::OwnedByRust(ptr),
borrow_handle: Cell::new(ptr::null_mut()),
}
}
pub fn new_weak(ptr: Weak<RefCell<T>>) -> Self {
Self {
shared_reference: SharedReferenceWithQml::OwnedByQml(ptr),
borrow_handle: Cell::new(ptr::null_mut()),
}
}
pub fn try_with_borrow<F, R>(&self, f: F) -> Result<R, RustObjAccessError>
where F:FnOnce(&T) -> R
{
let ptr_to_borrowed = self.borrow_handle.get();
if let Some(borrowed) = unsafe { ptr_to_borrowed.as_ref() } {
return Ok(f(borrowed.obj_ref.deref()))
};
let rc = self.shared_reference.get_rc()
.ok_or(RustObjAccessError::ExpiredWeakPtr)?;
let mut borrowed = RustObjBorrowHandle::new(rc, false)?;
self.borrow_handle.set(&mut borrowed);
let result = f(borrowed.obj_ref.deref());
self.borrow_handle.set(ptr::null_mut());
Ok(result)
}
pub fn try_with_borrow_mut<F, R>(&self, f: F) -> Result<R, RustObjAccessError>
where F:FnOnce(&mut T) -> R
{
let ptr_to_borrowed = self.borrow_handle.get();
if let Some(borrowed) = unsafe { ptr_to_borrowed.as_mut() } {
match &mut borrowed.obj_ref {
RustHandle::Immutable(_) |
RustHandle::ImmutableUnguardedBorrow(_) => {
let _ref = borrowed.obj_rc.try_borrow_mut()
.map_err(|err| RustObjAccessError::BorrowMutError(err))?;
unreachable!()
},
RustHandle::Mutable(_) |
RustHandle::MutableUnguardedBorrow(_) => {
return Ok(f(borrowed.obj_ref.deref_mut().unwrap()))
}
}
}
let rc = self.shared_reference.get_rc()
.ok_or(RustObjAccessError::ExpiredWeakPtr)?;
let mut borrowed = RustObjBorrowHandle::new(rc, true)?;
self.borrow_handle.set(&mut borrowed);
let result = f(borrowed.obj_ref.deref_mut().unwrap());
self.borrow_handle.set(ptr::null_mut());
Ok(result)
}
pub fn try_store_handle_and_call_qml<F, R>(&self, f: F) -> Result<R, RustObjAccessError>
where F:FnOnce(&T) -> R
{
let ptr_to_borrowed = self.borrow_handle.get();
if let Some(borrowed) = unsafe { ptr_to_borrowed.as_ref() } {
return Ok(f(borrowed.obj_ref.deref()))
}
let rc = self.shared_reference.get_rc()
.ok_or(RustObjAccessError::ExpiredWeakPtr)?;
{
match rc.try_borrow_mut() {
Ok(_) => return Err(RustObjAccessError::ExpectedBorrowed),
Err(_) => {},
}
}
let mut borrowed = RustObjBorrowHandle::new_unguarded(rc, false);
self.borrow_handle.set(&mut borrowed);
let result = f(borrowed.obj_ref.deref());
self.borrow_handle.set(ptr::null_mut());
Ok(result)
}
pub fn try_store_handle_and_call_qml_mut<F, R>(&self, f: F) -> Result<R, RustObjAccessError>
where F:FnOnce(&mut T) -> R
{
let ptr_to_borrowed = self.borrow_handle.get();
if let Some(borrowed) = unsafe { ptr_to_borrowed.as_mut() } {
match &mut borrowed.obj_ref {
RustHandle::Immutable(_) |
RustHandle::ImmutableUnguardedBorrow(_) => {
let _ref = borrowed.obj_rc.try_borrow_mut()
.map_err(|err| RustObjAccessError::BorrowMutError(err))?;
panic!("Object assumed to be borrowed but it is not")
},
RustHandle::Mutable(_) |
RustHandle::MutableUnguardedBorrow(_) => {
return Ok(f(borrowed.obj_ref.deref_mut().unwrap()))
}
}
}
let rc = self.shared_reference.get_rc()
.ok_or(RustObjAccessError::ExpiredWeakPtr)?;
{
}
let mut borrowed = RustObjBorrowHandle::new_unguarded(rc, true);
self.borrow_handle.set(&mut borrowed);
let result = f(borrowed.obj_ref.deref_mut().unwrap());
self.borrow_handle.set(ptr::null_mut());
Ok(result)
}
}
#[derive(Debug)]
pub enum RustObjAccessError {
BorrowError(BorrowError),
BorrowMutError(BorrowMutError),
ExpiredWeakPtr,
ExpectedBorrowed,
ExpectedBorrowedMut,
}
impl From<BorrowError> for RustObjAccessError {
fn from(value: BorrowError) -> Self {
Self::BorrowError(value)
}
}
impl From<BorrowMutError> for RustObjAccessError {
fn from(value: BorrowMutError) -> Self {
Self::BorrowMutError(value)
}
}
pub enum SharedReferenceWithQml<T: ?Sized> {
OwnedByRust(Rc<RefCell<T>>),
OwnedByQml(Weak<RefCell<T>>),
}
impl<T: ?Sized> SharedReferenceWithQml<T> {
fn get_rc(&self) -> Option<Rc<RefCell<T>>> {
match self {
SharedReferenceWithQml::OwnedByRust(rc) => Some(rc.clone()),
SharedReferenceWithQml::OwnedByQml(weak) => weak.upgrade()
}
}
}
struct RustObjBorrowHandle<'a, T: ?Sized> {
obj_rc: Rc<RefCell<T>>,
obj_ref: RustHandle<'a, T>,
}
impl<'a, T: ?Sized> RustObjBorrowHandle<'a, T> {
fn new(rc: Rc<RefCell<T>>, is_mutable: bool) -> Result<Self, RustObjAccessError> {
let mut uninit: MaybeUninit<Self> = MaybeUninit::uninit();
let ptr = uninit.as_mut_ptr();
unsafe {
addr_of_mut!((*ptr).obj_rc).write(rc.clone());
addr_of_mut!((*ptr).obj_ref).write(RustHandle::new(&(*ptr).obj_rc, is_mutable)?);
Ok(uninit.assume_init())
}
}
fn new_unguarded(rc: Rc<RefCell<T>>, is_mutable: bool) -> Self {
let mut uninit: MaybeUninit<Self> = MaybeUninit::uninit();
let ptr = uninit.as_mut_ptr();
unsafe {
addr_of_mut!((*ptr).obj_rc).write(rc.clone());
addr_of_mut!((*ptr).obj_ref).write(RustHandle::new_unguarded(&(*ptr).obj_rc, is_mutable));
uninit.assume_init()
}
}
}
enum RustHandle<'a, T: ?Sized> {
Immutable(Ref<'a, T>),
Mutable(RefMut<'a, T>),
ImmutableUnguardedBorrow(NonNull<T>),
MutableUnguardedBorrow(NonNull<T>),
}
impl<'a, T: ?Sized> RustHandle<'a, T> {
fn new(rc: &'a Rc<RefCell<T>>, is_mutable: bool) -> Result<Self, RustObjAccessError> {
match is_mutable {
true => Ok(Self::Mutable(
rc.try_borrow_mut()
.map_err(RustObjAccessError::from)?
)),
false => Ok(Self::Immutable(
rc.try_borrow()
.map_err(RustObjAccessError::from)?
))
}
}
fn new_unguarded(rc: &'a Rc<RefCell<T>>, is_mutable: bool) -> Self {
let nn = NonNull::new(rc.as_ptr()).unwrap();
match is_mutable {
true => Self::MutableUnguardedBorrow(nn),
false => Self::ImmutableUnguardedBorrow(nn),
}
}
fn deref(&self) -> &T {
match self {
Self::Immutable(ref_) => ref_,
Self::Mutable(ref_mut) => ref_mut,
Self::ImmutableUnguardedBorrow(ptr) => unsafe { ptr.as_ref() },
Self::MutableUnguardedBorrow(ptr) => unsafe {ptr.as_ref() },
}
}
fn deref_mut(&mut self) -> Option<&mut T> {
match self {
Self::Immutable(_) => None,
Self::ImmutableUnguardedBorrow(_) => None,
Self::Mutable(ref_mut) => Some(ref_mut),
Self::MutableUnguardedBorrow(ptr) => Some(unsafe {ptr.as_mut() }),
}
}
}