qtbridge_interfaces/object_access/
rust_object_access.rs1use std::mem;
5use std::rc::{Rc, Weak};
6use std::cell::{Cell, RefCell};
7
8#[macro_export]
9macro_rules! call_rust_trait_impl {
10 (mut $self:expr, $method:ident ( $($arg:expr),* )) => {
11 $self.rust_obj
12 .try_call_rust_with_handle_mut(|vtable| {
13 vtable.$method($($arg),*)
14 })
15 .expect(concat!(
16 "Failed to borrow mutably for ",
17 stringify!($method)
18 ))
19 };
20
21 ($self:expr, $method:ident ( $($arg:expr),* )) => {
22 $self.rust_obj
23 .try_call_rust_with_handle(|vtable| {
24 vtable.$method($($arg),*)
25 })
26 .expect(concat!(
27 "Failed to borrow for ",
28 stringify!($method)
29 ))
30 };
31}
32
33#[macro_export]
34macro_rules! call_cpp_impl {
35 (mut $self:expr, $mut_reference:expr, $method:ident ( $($arg:expr),* )) => {{
36 let proxy = unsafe {
37 $self.cpp_proxy
38 .as_mut()
39 .expect("cpp_proxy was null")
40 };
41 let proxy_pinned = unsafe { std::pin::Pin::new_unchecked(proxy) };
42 $self.rust_obj
43 .try_store_handle_and_call_cpp_mut($mut_reference, || proxy_pinned.$method($($arg),*))
44 .expect(concat!(
45 "Failed to borrow mutably for ",
46 stringify!($method)
47 ))
48 }};
49
50 ($self:expr, $reference:expr, $method:ident ( $($arg:expr),* )) => {{
51 let proxy = unsafe {
52 $self.cpp_proxy
53 .as_ref()
54 .expect("cpp_proxy was null")
55 };
56 $self.rust_obj
57 .try_store_handle_and_call_cpp($reference, || proxy.$method($($arg),*))
58 .expect(concat!(
59 "Failed to borrow for ",
60 stringify!($method)
61 ))
62 }};
63}
64
65pub struct RustObjAccess<T: ?Sized> {
73 shared_reference: SharedReferenceWithQml<T>,
74 borrow: Cell<BorrowState<T>>,
75}
76
77impl<T: ?Sized> RustObjAccess<T> {
78 pub fn new_strong(ptr: Rc<RefCell<T>>) -> Self {
81 Self {
82 shared_reference: SharedReferenceWithQml::OwnedByQml(ptr),
83 borrow: Cell::new(BorrowState::None),
84 }
85 }
86
87 pub fn new_weak(ptr: Weak<RefCell<T>>) -> Self {
90 Self {
91 shared_reference: SharedReferenceWithQml::OwnedByRust(ptr),
92 borrow: Cell::new(BorrowState::None),
93 }
94 }
95
96 pub fn try_call_rust_with_handle<F, R>(&self, f: F) -> Result<R, RustObjAccessError>
97 where
98 F: FnOnce(&T) -> R,
99 {
100 let guard = BorrowState::consume(&self.borrow);
101
102 match guard.content() {
103 BorrowState::Immutable(ptr) => Ok(f(unsafe { &**ptr })),
104 BorrowState::Mutable(ptr) => Ok(f(unsafe { &**ptr })),
105 BorrowState::None => {
106 let rc = self.shared_reference.get_rc()
107 .ok_or(RustObjAccessError::ExpiredWeakPtr)?;
108 let ref_guarded = rc.try_borrow()
109 .map_err(|err| RustObjAccessError::BorrowError(err))?;
110 Ok(f(&*ref_guarded))
111 }
112 }
113 }
114
115 pub fn try_call_rust_with_handle_mut<F, R>(&self, f: F) -> Result<R, RustObjAccessError>
116 where
117 F: FnOnce(&mut T) -> R,
118 {
119 let guard = BorrowState::consume(&self.borrow);
120
121 match guard.content() {
122 BorrowState::Mutable(ptr) => Ok(f(unsafe { &mut **ptr })),
123 BorrowState::Immutable(_) => Err(RustObjAccessError::BorrowConflict),
124 BorrowState::None => {
125 let rc = self.shared_reference.get_rc()
126 .ok_or(RustObjAccessError::ExpiredWeakPtr)?;
127 let mut ref_guarded = rc.try_borrow_mut()
128 .map_err(|err| RustObjAccessError::BorrowMutError(err))?;
129 Ok(f(&mut *ref_guarded))
130 }
131 }
132 }
133
134 pub fn try_store_handle_and_call_cpp<F, R>(&self, rust_obj: &T, f: F) -> Result<R, RustObjAccessError>
135 where
136 F: FnOnce() -> R,
137 {
138 assert!(
139 self.shared_reference.contains(rust_obj),
140 "The rust_obj you want to call a function on does not match the shared reference."
141 );
142 let guard = BorrowState::store(&self.borrow, rust_obj);
143 if matches!(guard.content(), BorrowState::Mutable(_)) {
145 return Err(RustObjAccessError::BorrowConflict);
146 }
147 Ok(f())
148 }
149
150 pub fn try_store_handle_and_call_cpp_mut<F, R>(&self, rust_obj: &mut T, f: F) -> Result<R, RustObjAccessError>
151 where
152 F: FnOnce() -> R,
153 {
154 assert!(
155 self.shared_reference.contains(rust_obj),
156 "The rust_obj you want to call a function on does not match the shared reference."
157 );
158 let guard = BorrowState::store_mut(&self.borrow, rust_obj);
159 if matches!(guard.content(), BorrowState::Mutable(_)) || matches!(guard.content(), BorrowState::Immutable(_)) {
161 return Err(RustObjAccessError::BorrowConflict);
162 }
163 Ok(f())
164 }
165
166 pub fn get_rc(&self) -> Option<Rc<RefCell<T>>> {
167 self.shared_reference.get_rc()
168 }
169}
170
171
172enum BorrowState<T: ?Sized> {
173 None,
174 Immutable(*const T),
175 Mutable(*mut T),
176}
177
178impl<T: ?Sized> BorrowState<T> {
179 fn consume(cell: &Cell<Self>) -> BorrowGuard<'_, T> {
180 let consumed = cell.replace(BorrowState::None);
181 BorrowGuard { cell, consumed }
182 }
183
184 fn store<'a>(cell: &'a Cell<Self>, rust_obj: &T) -> BorrowGuard<'a, T> {
185 let old = cell.replace(BorrowState::Immutable(rust_obj as *const T));
186 BorrowGuard { cell, consumed: old }
187 }
188
189 fn store_mut<'a>(cell: &'a Cell<Self>, rust_obj: &mut T) -> BorrowGuard<'a, T> {
190 let old = cell.replace(BorrowState::Mutable(rust_obj as *mut T));
191 BorrowGuard { cell, consumed: old }
192 }
193}
194
195struct BorrowGuard<'a, T: ?Sized> {
196 cell: &'a Cell<BorrowState<T>>,
197 consumed: BorrowState<T>,
198}
199
200impl<T: ?Sized> BorrowGuard<'_, T> {
201 fn content(&self) -> &BorrowState<T> {
202 &self.consumed
203 }
204}
205
206impl<T: ?Sized> Drop for BorrowGuard<'_, T> {
207 fn drop(&mut self) {
208 self.cell.set(mem::replace(&mut self.consumed, BorrowState::None));
209 }
210}
211
212#[derive(Debug)]
213pub enum RustObjAccessError {
214 BorrowError(std::cell::BorrowError),
215 BorrowMutError(std::cell::BorrowMutError),
216 BorrowConflict,
217 ExpiredWeakPtr,
218}
219
220enum SharedReferenceWithQml<T: ?Sized> {
222 OwnedByRust(Weak<RefCell<T>>),
226
227 OwnedByQml(Rc<RefCell<T>>),
232}
233
234impl<T: ?Sized> SharedReferenceWithQml<T> {
235 fn get_rc(&self) -> Option<Rc<RefCell<T>>> {
236 match self {
237 SharedReferenceWithQml::OwnedByRust(weak) => weak.upgrade(),
238 SharedReferenceWithQml::OwnedByQml(rc) => Some(rc.clone()),
239 }
240 }
241
242 fn contains(&self, obj: &T) -> bool {
243 let Some(rc) = self.get_rc() else { return false };
244 let expected = rc.as_ptr() as *const ();
245 let actual = obj as *const T as *const ();
246 expected == actual
247 }
248}