1use std::cell::UnsafeCell;
23use std::marker::PhantomData;
24
25use tvm_ffi_sys::{TVMFFIAny, TVMFFIObject, TVMFFITypeIndex as TypeIndex};
26
27use crate::any::ArgTryFromAnyView;
28use crate::{AnyCompatible, AnyView, Error, ObjectRefCore, Result};
29
30pub struct RValueRef<T>
35where
36 T: ObjectRefCore + AnyCompatible,
37{
38 slot: UnsafeCell<*mut TVMFFIObject>,
39 _marker: PhantomData<T>,
40}
41
42impl<T> RValueRef<T>
43where
44 T: ObjectRefCore + AnyCompatible,
45{
46 pub fn new(value: T) -> Self {
48 let mut raw = TVMFFIAny::new();
49 unsafe { T::move_to_any(value, &mut raw) };
50 debug_assert!(raw.type_index >= TypeIndex::kTVMFFIStaticObjectBegin as i32);
51 Self {
52 slot: UnsafeCell::new(unsafe { raw.data_union.v_obj }),
53 _marker: PhantomData,
54 }
55 }
56
57 pub fn into_inner(mut self) -> T {
59 let object = *self.slot.get_mut();
60 assert!(!object.is_null(), "RValueRef has already been moved");
61 *self.slot.get_mut() = std::ptr::null_mut();
62 unsafe {
63 let mut raw = object_any(object);
64 T::move_from_any_after_check(&mut raw)
65 }
66 }
67
68 unsafe fn from_view(value: &AnyView<'_>, arg_index: Option<usize>) -> Result<Self> {
69 let raw = value.as_raw_ffi_any();
70 let converted = if raw.type_index == TypeIndex::kTVMFFIObjectRValueRef as i32 {
71 let slot = raw.data_union.v_ptr.cast::<*mut TVMFFIObject>();
72 if slot.is_null() || (*slot).is_null() {
73 return Err(conversion_error::<T>(raw, arg_index, true));
74 }
75 let object = *slot;
76 let object_view = object_any(object);
77 if T::check_any_strict(&object_view) {
78 *slot = std::ptr::null_mut();
79 return Ok(Self {
80 slot: UnsafeCell::new(object),
81 _marker: PhantomData,
82 });
83 }
84 T::try_cast_from_any_view(&object_view)
85 } else if T::check_any_strict(raw) {
86 Ok(T::copy_from_any_view_after_check(raw))
87 } else {
88 T::try_cast_from_any_view(raw)
89 };
90
91 converted
92 .map(Self::new)
93 .map_err(|()| conversion_error::<T>(raw, arg_index, false))
94 }
95}
96
97impl<T> From<T> for RValueRef<T>
98where
99 T: ObjectRefCore + AnyCompatible,
100{
101 fn from(value: T) -> Self {
102 Self::new(value)
103 }
104}
105
106impl<'a, T> From<&'a RValueRef<T>> for AnyView<'a>
107where
108 T: ObjectRefCore + AnyCompatible,
109{
110 fn from(value: &'a RValueRef<T>) -> Self {
111 let mut raw = TVMFFIAny::new();
112 raw.type_index = TypeIndex::kTVMFFIObjectRValueRef as i32;
113 raw.data_union.v_ptr = value.slot.get().cast();
114 unsafe { AnyView::from_raw_ffi_any(raw) }
115 }
116}
117
118impl<T> TryFrom<AnyView<'_>> for RValueRef<T>
119where
120 T: ObjectRefCore + AnyCompatible,
121{
122 type Error = Error;
123
124 fn try_from(value: AnyView<'_>) -> Result<Self> {
125 unsafe { Self::from_view(&value, None) }
126 }
127}
128
129impl<T> ArgTryFromAnyView for RValueRef<T>
130where
131 T: ObjectRefCore + AnyCompatible,
132{
133 fn try_from_any_view(value: &AnyView<'_>, arg_index: usize) -> Result<Self> {
134 unsafe { Self::from_view(value, Some(arg_index)) }
135 }
136}
137
138impl<T> Drop for RValueRef<T>
139where
140 T: ObjectRefCore + AnyCompatible,
141{
142 fn drop(&mut self) {
143 let object = *self.slot.get_mut();
144 if !object.is_null() {
145 unsafe { crate::object::unsafe_::dec_ref(object) };
146 }
147 }
148}
149
150unsafe fn object_any(object: *mut TVMFFIObject) -> TVMFFIAny {
151 let mut raw = TVMFFIAny::new();
152 raw.type_index = (*object).type_index;
153 raw.data_union.v_obj = object;
154 raw
155}
156
157unsafe fn conversion_error<T>(
158 raw: &TVMFFIAny,
159 arg_index: Option<usize>,
160 already_moved: bool,
161) -> Error
162where
163 T: ObjectRefCore + AnyCompatible,
164{
165 let source = if already_moved {
166 "an already-moved RValueRef".to_string()
167 } else if raw.type_index == TypeIndex::kTVMFFIObjectRValueRef as i32 {
168 "RValueRef with an incompatible object type".to_string()
169 } else {
170 T::get_mismatch_type_info(raw)
171 };
172 let prefix = arg_index
173 .map(|index| format!("Argument #{index}: "))
174 .unwrap_or_default();
175 Error::new(
176 crate::error::TYPE_ERROR,
177 &format!(
178 "{prefix}Cannot convert from `{source}` to `RValueRef<{}>`",
179 T::type_str()
180 ),
181 "",
182 )
183}