1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
use crate::{QObject, QPtr};
use cpp_core::{
    CastFrom, CastInto, CppBox, CppDeletable, DynamicCast, Ptr, Ref, StaticDowncast, StaticUpcast,
};
use std::ops::Deref;
use std::{fmt, mem};

/// An owning pointer for `QObject`-based objects.
///
/// `QBox` will delete its object on drop if it has no parent. If the object has a parent,
/// it's assumed that the parent is responsible for deleting the object, as per Qt ownership system.
/// Additionally, `QBox` will be automatically set to null when the object is deleted, similar
/// to `QPtr` (or `QPointer<T>` in C++). `QBox` will not attempt to delete null pointers.
///
/// Note that dereferencing a null `QBox` will panic, so if it's known that the object may
/// already have been deleted, you should use `is_null()`, `as_ref()`,
/// or a similar method to check
/// if the object is still alive before calling its methods.
///
/// Unlike `CppBox` (which is non-nullable), `QBox` is permitted to contain a null pointer because
/// even if a non-null pointer is provided when constructing `QBox`, it will become null
/// automatically if the object is deleted.
///
/// To prevent the object from being deleted, convert `QBox` to another type of pointer using
/// `into_q_ptr()` or `into_ptr()`. Alternatively, setting a parent for the object will prevent
/// `QBox` from deleting it.
///
/// To make sure the object is deleted regardless of its parent, convert `QBox` to `CppBox` using
/// `into_box()`.
///
/// # Safety
///
/// `QBox` has the same safety issues as `QPtr`. See `QPtr` documentation.
pub struct QBox<T: StaticUpcast<QObject> + CppDeletable>(QPtr<T>);

impl<T: StaticUpcast<QObject> + CppDeletable> QBox<T> {
    /// Creates a `QBox` from a `QPtr`.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn from_q_ptr(target: QPtr<T>) -> Self {
        QBox(target)
    }

    /// Creates a `QBox` from a `Ptr`.
    ///
    /// ### Safety
    ///
    /// `target` must be either a valid pointer to an object or a null pointer.
    /// See type level documentation.
    pub unsafe fn new(target: impl CastInto<Ptr<T>>) -> Self {
        QBox::from_q_ptr(QPtr::new(target))
    }

    /// Creates a `QBox` from a raw pointer.
    ///
    /// ### Safety
    ///
    /// `target` must be either a valid pointer to an object or a null pointer.
    /// See type level documentation.
    pub unsafe fn from_raw(target: *const T) -> Self {
        QBox::from_q_ptr(QPtr::from_raw(target))
    }

    /// Creates a null pointer.
    ///
    /// Note that you can also use `NullPtr` to specify a null pointer to a function accepting
    /// `impl CastInto<Ptr<_>>`. Unlike `Ptr`, `NullPtr` is not a generic type, so it will
    /// not cause type inference issues.
    ///
    /// Note that accessing the content of a null `QBox` through `Deref` will result
    /// in a panic.
    ///
    /// ### Safety
    ///
    /// Null pointers must not be dereferenced. See type level documentation.
    pub unsafe fn null() -> Self {
        QBox::from_q_ptr(QPtr::<T>::null())
    }

    /// Returns true if the pointer is null.
    pub unsafe fn is_null(&self) -> bool {
        self.0.is_null()
    }

    /// Returns the content as a const `Ptr`.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn as_ptr(&self) -> Ptr<T> {
        self.0.as_ptr()
    }

    /// Returns the content as a raw const pointer.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn as_raw_ptr(&self) -> *const T {
        self.0.as_raw_ptr()
    }

    /// Returns the content as a raw mutable pointer.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn as_mut_raw_ptr(&self) -> *mut T {
        self.0.as_mut_raw_ptr()
    }

    /// Returns the content as a const `Ref`. Returns `None` if `self` is a null pointer.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn as_ref(&self) -> Option<Ref<T>> {
        self.0.as_ref()
    }

    /// Returns a reference to the value. Returns `None` if the pointer is null.
    ///
    /// ### Safety
    ///
    /// `self` must be valid.
    /// The content must not be read or modified through other ways while the returned reference
    /// exists.See type level documentation.
    pub unsafe fn as_raw_ref<'a>(&self) -> Option<&'a T> {
        self.as_ref().map(|r| r.as_raw_ref())
    }

    /// Returns a mutable reference to the value. Returns `None` if the pointer is null.
    ///
    /// ### Safety
    ///
    /// `self` must be valid.
    /// The content must not be read or modified through other ways while the returned reference
    /// exists.See type level documentation.
    pub unsafe fn as_mut_raw_ref<'a>(&self) -> Option<&'a mut T> {
        self.as_ref().map(|r| r.as_mut_raw_ref())
    }

    /// Converts the pointer to the base class type `U`.
    ///
    /// ### Safety
    ///
    /// This operation is safe as long as `self` is valid or null. See type level documentation.
    pub unsafe fn static_upcast<U>(&self) -> QPtr<U>
    where
        T: StaticUpcast<U>,
        U: StaticUpcast<QObject>,
    {
        QPtr::<U>::new(self.as_ptr().static_upcast::<U>())
    }

    /// Converts the pointer to the derived class type `U`.
    ///
    /// It's recommended to use `dynamic_cast` instead because it performs a checked conversion.
    ///
    /// ### Safety
    ///
    /// This operation is safe as long as `self` is valid and it's type is `U` or inherits from `U`,
    /// of if `self` is a null pointer. See type level documentation.
    pub unsafe fn static_downcast<U>(&self) -> QPtr<U>
    where
        T: StaticDowncast<U>,
        U: StaticUpcast<QObject>,
    {
        QPtr::<U>::new(self.as_ptr().static_downcast())
    }

    /// Converts the pointer to the derived class type `U`. Returns `None` if the object's type
    /// is not `U` and doesn't inherit `U`.
    ///
    /// ### Safety
    ///
    /// This operation is safe as long as `self` is valid or null. See type level documentation.
    pub unsafe fn dynamic_cast<U>(&self) -> QPtr<U>
    where
        T: DynamicCast<U>,
        U: StaticUpcast<QObject>,
    {
        QPtr::<U>::new(self.as_ptr().dynamic_cast())
    }

    /// Converts this pointer to a `CppBox`. Returns `None` if `self`
    /// is a null pointer.
    ///
    /// Unlike `QBox`, `CppBox` will always delete the object when dropped.
    ///
    /// ### Safety
    ///
    /// `CppBox` will attempt to delete the object on drop. If something else also tries to
    /// delete this object before or after that, the behavior is undefined.
    /// See type level documentation.
    pub unsafe fn into_box(self) -> Option<CppBox<T>> {
        self.into_q_ptr().to_box()
    }

    /// Converts this `QBox` into a `QPtr`.
    ///
    /// Unlike `QBox`, `QPtr` will never delete the object when dropped.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn into_q_ptr(mut self) -> QPtr<T> {
        mem::replace(&mut self.0, QPtr::null())
    }

    /// Converts this `QBox` into a `Ptr`.
    ///
    /// Unlike `QBox`, `Ptr` will never delete the object when dropped.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn into_ptr(self) -> Ptr<T> {
        self.into_q_ptr().as_ptr()
    }

    /// Converts this `QBox` into a raw pointer without deleting the object.
    ///
    /// ### Safety
    ///
    /// See type level documentation.
    pub unsafe fn into_raw_ptr(self) -> *mut T {
        self.into_q_ptr().as_mut_raw_ptr()
    }
}

impl<T: StaticUpcast<QObject> + CppDeletable> fmt::Debug for QBox<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "QBox({:?})", unsafe { self.as_raw_ptr() })
    }
}

/// Allows to call member functions of `T` and its base classes directly on the pointer.
///
/// Panics if the pointer is null.
impl<T: StaticUpcast<QObject> + CppDeletable> Deref for QBox<T> {
    type Target = T;

    fn deref(&self) -> &T {
        unsafe {
            let ptr = self.as_raw_ptr();
            if ptr.is_null() {
                panic!("attempted to deref a null QBox<T>");
            }
            &*ptr
        }
    }
}

impl<'a, T, U> CastFrom<&'a QBox<U>> for Ptr<T>
where
    U: StaticUpcast<T> + StaticUpcast<QObject> + CppDeletable,
{
    unsafe fn cast_from(value: &'a QBox<U>) -> Self {
        CastFrom::cast_from(value.as_ptr())
    }
}

impl<T: StaticUpcast<QObject> + CppDeletable> Drop for QBox<T> {
    fn drop(&mut self) {
        unsafe {
            let ptr = self.as_ptr();
            if !ptr.is_null() && ptr.static_upcast().parent().is_null() {
                T::delete(&*ptr.as_raw_ptr());
            }
        }
    }
}