Skip to main content

qtbridge_runtime/
qmetatype_compatible.rs

1// Copyright (C) 2026 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only
3
4use std::cell::RefCell;
5use std::rc::Rc;
6
7use qtbridge_type_lib::{QList, QList_QString, QMetaType, QObject, QObjectList, QString};
8
9#[cfg(feature = "serde_json")]
10use qtbridge_type_lib::{QJsonArray, QJsonValue};
11
12use crate::{QMetaTypeGet, QObjectHolder, QmlElement};
13
14/// Enables a type to be used as a meta call argument and to be convertible from/to QVariant.
15///
16/// Implemented for:
17/// - Primitive numeric types and `bool`
18/// - [`String`]
19/// - [`Vec<T>`] where `T` is one of the above
20/// - [`Rc<RefCell<T>>`] where `T` implements [`QObjectHolder`]
21/// - [`Vec<Rc<RefCell<T>>>`] where `T` implements [`QmlElement`]
22///
23pub trait QMetaTypeCompatible {
24    type CompatibleType: QMetaTypeGet;
25
26    fn to_compatible(&self) -> Self::CompatibleType;
27    fn from_compatible(from: &Self::CompatibleType) -> Self;
28    fn compatible_qmetatype() -> QMetaType {
29        Self::CompatibleType::get_qmetatype()
30    }
31}
32
33macro_rules! impl_primitive_direct {
34    ($($t:ty),*) => {
35        $(impl QMetaTypeCompatible for $t {
36            type CompatibleType = $t;
37            fn to_compatible(&self) -> Self::CompatibleType { *self }
38            fn from_compatible(from: &Self::CompatibleType) -> Self { *from }
39        })*
40    }
41}
42impl_primitive_direct!(bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64);
43
44macro_rules! impl_primitive_convert {
45    ($($t:ty => $compat:ty),*) => {
46        $(impl QMetaTypeCompatible for $t {
47            type CompatibleType = $compat;
48            fn to_compatible(&self) -> Self::CompatibleType { *self as $compat }
49            fn from_compatible(from: &Self::CompatibleType) -> Self { *from as Self }
50        })*
51    }
52}
53#[cfg(target_pointer_width = "64")]
54impl_primitive_convert!(
55    isize => i64,
56    usize => u64
57);
58#[cfg(target_pointer_width = "32")]
59impl_primitive_convert!(
60    isize => i32,
61    usize => u32
62);
63
64// The CompatibleType pointer is safe single-threaded: arguments outlive delivery on
65// the emitting stack frame, and no collection point runs between a return
66// value leaving Rust and the engine taking ownership. Revisit when signals
67// can cross threads.
68macro_rules! impl_vec_direct {
69    ($($t:ty),*) => {
70        $(impl QMetaTypeCompatible for Vec<$t> {
71            type CompatibleType = QList<$t>;
72            fn to_compatible(&self) -> Self::CompatibleType { self.into_iter().collect() }
73            fn from_compatible(from: &Self::CompatibleType) -> Self { from.into() }
74        })*
75    }
76}
77impl_vec_direct!(bool, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64);
78
79macro_rules! impl_vec_convert {
80    ($($t:ty => $compat:ty),*) => {
81        $(impl QMetaTypeCompatible for Vec<$t> {
82            type CompatibleType = QList<$compat>;
83            fn to_compatible(&self) -> Self::CompatibleType {
84                self.iter()
85                    .map(|arg| *arg as $compat)
86                    .collect()
87            }
88            fn from_compatible(from: &Self::CompatibleType) -> Self {
89                from.iter()
90                    .map(|arg| *arg as $t)
91                    .collect()
92            }
93        })*
94    }
95}
96#[cfg(target_pointer_width = "64")]
97impl_vec_convert!(
98    isize => i64,
99    usize => u64
100);
101#[cfg(target_pointer_width = "32")]
102impl_vec_convert!(
103    isize => i32,
104    usize => u32
105);
106
107impl QMetaTypeCompatible for String {
108    type CompatibleType = QString;
109    fn to_compatible(&self) -> QString { self.into() }
110    fn from_compatible(from: &QString) -> String { from.into() }
111}
112
113impl QMetaTypeCompatible for Vec<String> {
114    type CompatibleType = QList_QString;
115
116    fn to_compatible(&self) -> Self::CompatibleType {
117        self.iter()
118            .map(QString::from)
119            .collect()
120    }
121
122    fn from_compatible(from: &Self::CompatibleType) -> Self {
123        from.iter()
124            .map(String::from)
125            .collect()
126    }
127}
128
129impl<T: QObjectHolder> QMetaTypeCompatible for Rc<RefCell<T>> {
130    type CompatibleType = *mut QObject;
131
132    fn to_compatible(&self) -> *mut QObject {
133        T::rc_ref_cell_to_qobject(self).cast_mut()
134    }
135
136    fn from_compatible(from: &*mut QObject) -> Self {
137        unsafe { T::qobject_to_rc_ref_cell(*from) }
138    }
139
140    fn compatible_qmetatype() -> QMetaType {
141        <T as QObjectHolder>::get_qobject_ptr_qmetatype()
142    }
143}
144
145// The compatible pointers share the in-flight guarantee of Rc<RefCell<T>> above.
146impl<T: QmlElement> QMetaTypeCompatible for Vec<Rc<RefCell<T>>> {
147    type CompatibleType = QObjectList;
148
149    fn to_compatible(&self) -> QObjectList {
150        self.iter()
151            .map(|rc| unsafe { QObject::to_cxx_qt(T::rc_ref_cell_to_qobject(rc).cast_mut()) })
152            .collect()
153    }
154
155    fn from_compatible(from: &QObjectList) -> Self {
156        from.iter()
157            .map(|ptr| unsafe { T::qobject_to_rc_ref_cell(QObject::ptr_from_cxx_qt(ptr)) })
158            .collect()
159    }
160}
161
162#[cfg(feature = "serde_json")]
163impl QMetaTypeCompatible for serde_json::Value {
164    type CompatibleType = QJsonValue;
165    fn to_compatible(&self) -> QJsonValue { crate::serde_tools::serde_to_qjsonvalue(self) }
166    fn from_compatible(from: &QJsonValue) -> serde_json::Value { crate::serde_tools::qjsonvalue_to_serde(from) }
167}
168
169#[cfg(feature = "serde_json")]
170impl QMetaTypeCompatible for Vec<serde_json::Value> {
171    type CompatibleType = QJsonArray;
172    fn to_compatible(&self) -> QJsonArray { crate::serde_tools::serde_to_qjsonarray(self) }
173    fn from_compatible(from: &QJsonArray) -> Vec<serde_json::Value> {
174        from.iter()
175            .map(|jv| crate::serde_tools::qjsonvalue_to_serde(&jv))
176            .collect()
177    }
178}