1use std::ops::Deref;
2use std::ptr;
3
4use super::{check_status, Value};
5use crate::{bindgen_runtime::ToNapiValue, sys, Env, Result};
6
7pub struct Ref<T> {
8 pub(crate) raw_ref: sys::napi_ref,
9 pub(crate) count: u32,
10 pub(crate) inner: T,
11}
12
13#[allow(clippy::non_send_fields_in_send_ty)]
14unsafe impl<T> Send for Ref<T> {}
15unsafe impl<T> Sync for Ref<T> {}
16
17impl<T> Ref<T> {
18 pub(crate) fn new(js_value: Value, ref_count: u32, inner: T) -> Result<Ref<T>> {
19 let mut raw_ref = ptr::null_mut();
20 assert_ne!(ref_count, 0, "Initial `ref_count` must be > 0");
21 check_status!(unsafe {
22 sys::napi_create_reference(js_value.env, js_value.value, ref_count, &mut raw_ref)
23 })?;
24 Ok(Ref {
25 raw_ref,
26 count: ref_count,
27 inner,
28 })
29 }
30
31 pub fn reference(&mut self, env: &Env) -> Result<u32> {
32 check_status!(unsafe { sys::napi_reference_ref(env.0, self.raw_ref, &mut self.count) })?;
33 Ok(self.count)
34 }
35
36 pub fn unref(&mut self, env: Env) -> Result<u32> {
37 check_status!(unsafe { sys::napi_reference_unref(env.0, self.raw_ref, &mut self.count) })?;
38
39 if self.count == 0 {
40 check_status!(unsafe { sys::napi_delete_reference(env.0, self.raw_ref) })?;
41 }
42 Ok(self.count)
43 }
44}
45
46impl<T> Deref for Ref<T> {
47 type Target = T;
48
49 fn deref(&self) -> &T {
50 &self.inner
51 }
52}
53
54#[cfg(debug_assertions)]
55impl<T> Drop for Ref<T> {
56 fn drop(&mut self) {
57 debug_assert_eq!(
58 self.count, 0,
59 "Ref count is not equal to 0 while dropping Ref, potential memory leak"
60 );
61 }
62}
63
64impl<T: 'static> ToNapiValue for Ref<T> {
65 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
66 let mut result: *mut napi_value__ = ptr::null_mut();
67 check_status!(
68 unsafe { sys::napi_get_reference_value(env, val.raw_ref, &mut result) },
69 "Failed to get reference value"
70 )?;
71 Ok(result)
72 }
73}
74