1use crate::{bindgen_prelude::*, check_status, check_status_and_type, sys, Error, Result, Status};
2
3use std::ffi::{c_void, CStr};
4use std::fmt::Display;
5use std::mem;
6use std::ops::Deref;
7use std::ptr;
8
9impl TypeName for String {
10 fn type_name() -> &'static str {
11 "String"
12 }
13
14 fn value_type() -> ValueType {
15 ValueType::String
16 }
17}
18
19impl ValidateNapiValue for String {}
20
21impl ToNapiValue for &String {
22 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
23 let mut ptr: *mut napi_value__ = ptr::null_mut();
24
25 check_status!(
26 unsafe { sys::napi_create_string_utf8(env, val.as_ptr().cast(), val.len(), &mut ptr) },
27 "Failed to convert rust `String` into napi `string`"
28 )?;
29
30 Ok(ptr)
31 }
32}
33
34impl ToNapiValue for String {
35 #[inline]
36 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
37 #[allow(clippy::needless_borrows_for_generic_args)]
38 unsafe {
39 ToNapiValue::to_napi_value(env, &val)
40 }
41 }
42}
43
44impl FromNapiValue for String {
45 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
46 let mut len = 0;
47
48 check_status_and_type!(
49 unsafe { sys::napi_get_value_string_utf8(env, napi_val, ptr::null_mut(), 0, &mut len) },
50 env,
51 napi_val,
52 "Failed to convert JavaScript value `{}` into rust type `String`"
53 )?;
54
55 // end char len in C
56 len += 1;
57 let mut ret = Vec::with_capacity(len);
58 let buf_ptr = ret.as_mut_ptr();
59
60 let mut written_char_count = 0;
61
62 check_status_and_type!(
63 unsafe {
64 sys::napi_get_value_string_utf8(env, napi_val, buf_ptr, len, &mut written_char_count)
65 },
66 env,
67 napi_val,
68 "Failed to convert napi `{}` into rust type `String`"
69 )?;
70
71 let mut ret = mem::ManuallyDrop::new(ret);
72 let buf_ptr = ret.as_mut_ptr();
73 let bytes = unsafe { Vec::from_raw_parts(buf_ptr as *mut u8, written_char_count, len) };
74 Ok(unsafe { String::from_utf8_unchecked(bytes) })
75 }
76}
77
78impl TypeName for &str {
79 fn type_name() -> &'static str {
80 "String"
81 }
82
83 fn value_type() -> ValueType {
84 ValueType::String
85 }
86}
87
88impl ValidateNapiValue for &str {}
89
90impl FromNapiValue for &str {
91 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
92 let mut len = 0;
93
94 check_status_and_type!(
95 unsafe { sys::napi_get_value_string_utf8(env, napi_val, ptr::null_mut(), 0, &mut len) },
96 env,
97 napi_val,
98 "Failed to convert napi `{}` into rust type `String`"
99 )?;
100
101 // end char len in C
102 len += 1;
103 let mut ret = Vec::with_capacity(len);
104 let buf_ptr = ret.as_mut_ptr();
105 let mut written_char_count = 0;
106
107 check_status_and_type!(
108 unsafe {
109 sys::napi_get_value_string_utf8(env, napi_val, buf_ptr, len, &mut written_char_count)
110 },
111 env,
112 napi_val,
113 "Failed to convert JavaScript value `{}` into rust type `String`"
114 )?;
115
116 // The `&str` should only be accepted from function arguments.
117 // We shouldn't implement `FromNapiValue` for it before.
118 // When it's used with `Object.get` scenario, the memory which `&str` point to will be invalid.
119 // For this scenario, we create a temporary empty `Object` here and assign the `Vec<u8>` under `&str` to it.
120 // So we can safely forget the `Vec<u8>` here which could fix the memory issue here.
121 // FIXME: This implementation should be removed in next major release.
122 let mut temporary_external_object = ptr::null_mut();
123 check_status!(unsafe {
124 sys::napi_create_external(
125 env,
126 buf_ptr as *mut c_void,
127 Some(release_string),
128 Box::into_raw(Box::new(len)) as *mut c_void,
129 &mut temporary_external_object,
130 )
131 })?;
132
133 std::mem::forget(ret);
134 match unsafe { CStr::from_ptr(buf_ptr) }.to_str() {
135 Err(e) => Err(Error::new(
136 Status::InvalidArg,
137 format!("Failed to read utf8 string, {}", e),
138 )),
139 Ok(s) => Ok(s),
140 }
141 }
142}
143
144impl ToNapiValue for &str {
145 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
146 unsafe { String::to_napi_value(env, val.to_owned()) }
147 }
148}
149
150#[derive(Debug)]
151pub struct Utf16String(String);
152
153impl ValidateNapiValue for Utf16String {}
154
155impl From<String> for Utf16String {
156 fn from(s: String) -> Self {
157 Utf16String(s)
158 }
159}
160
161impl Display for Utf16String {
162 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163 write!(f, "{}", self.0)
164 }
165}
166
167impl Deref for Utf16String {
168 type Target = str;
169
170 fn deref(&self) -> &Self::Target {
171 self.0.as_ref()
172 }
173}
174
175impl TypeName for Utf16String {
176 fn type_name() -> &'static str {
177 "String(utf16)"
178 }
179
180 fn value_type() -> ValueType {
181 ValueType::String
182 }
183}
184
185impl FromNapiValue for Utf16String {
186 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
187 let mut len = 0;
188
189 check_status!(
190 unsafe { sys::napi_get_value_string_utf16(env, napi_val, ptr::null_mut(), 0, &mut len) },
191 "Failed to convert napi `utf16 string` into rust type `String`",
192 )?;
193
194 // end char len in C
195 len += 1;
196 let mut ret = vec![0; len];
197 let mut written_char_count = 0;
198
199 check_status!(
200 unsafe {
201 sys::napi_get_value_string_utf16(
202 env,
203 napi_val,
204 ret.as_mut_ptr(),
205 len,
206 &mut written_char_count,
207 )
208 },
209 "Failed to convert napi `utf16 string` into rust type `String`",
210 )?;
211
212 let (_, ret) = ret.split_last().unwrap_or((&0, &[]));
213
214 match String::from_utf16(ret) {
215 Err(e) => Err(Error::new(
216 Status::InvalidArg,
217 format!("Failed to read utf16 string, {}", e),
218 )),
219 Ok(s) => Ok(Utf16String(s)),
220 }
221 }
222}
223
224impl ToNapiValue for Utf16String {
225 unsafe fn to_napi_value(env: sys::napi_env, val: Utf16String) -> Result<sys::napi_value> {
226 let mut ptr: *mut napi_value__ = ptr::null_mut();
227
228 let encoded: Vec = val.0.encode_utf16().collect::<Vec<_>>();
229
230 check_status!(
231 unsafe {
232 sys::napi_create_string_utf16(env, encoded.as_ptr() as *const _, encoded.len(), &mut ptr)
233 },
234 "Failed to convert napi `string` into rust type `String`"
235 )?;
236
237 Ok(ptr)
238 }
239}
240
241#[cfg(feature = "latin1")]
242pub mod latin1_string {
243 use super::*;
244
245 #[derive(Debug)]
246 pub struct Latin1String(String);
247
248 impl ValidateNapiValue for Latin1String {}
249
250 impl From<String> for Latin1String {
251 fn from(s: String) -> Self {
252 Latin1String(s)
253 }
254 }
255
256 impl Display for Latin1String {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258 write!(f, "{}", self.0)
259 }
260 }
261
262 impl Deref for Latin1String {
263 type Target = str;
264
265 fn deref(&self) -> &Self::Target {
266 self.0.as_ref()
267 }
268 }
269
270 impl TypeName for Latin1String {
271 fn type_name() -> &'static str {
272 "String(latin1)"
273 }
274
275 fn value_type() -> ValueType {
276 ValueType::String
277 }
278 }
279
280 impl FromNapiValue for Latin1String {
281 unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
282 let mut len = 0;
283
284 check_status!(
285 unsafe { sys::napi_get_value_string_latin1(env, napi_val, ptr::null_mut(), 0, &mut len) },
286 "Failed to convert napi `latin1 string` into rust type `String`",
287 )?;
288
289 // end char len in C
290 len += 1;
291 let mut buf = Vec::with_capacity(len);
292 let buf_ptr = buf.as_mut_ptr();
293
294 let mut written_char_count = 0;
295
296 mem::forget(buf);
297
298 check_status!(
299 unsafe {
300 sys::napi_get_value_string_latin1(env, napi_val, buf_ptr, len, &mut written_char_count)
301 },
302 "Failed to convert napi `latin1 string` into rust type `String`"
303 )?;
304
305 let buf =
306 unsafe { Vec::from_raw_parts(buf_ptr as *mut _, written_char_count, written_char_count) };
307 let mut dst_slice = vec![0; buf.len() * 2];
308 let written =
309 encoding_rs::mem::convert_latin1_to_utf8(buf.as_slice(), dst_slice.as_mut_slice());
310 dst_slice.truncate(written);
311
312 Ok(Latin1String(unsafe {
313 String::from_utf8_unchecked(dst_slice)
314 }))
315 }
316 }
317
318 impl ToNapiValue for Latin1String {
319 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
320 let mut ptr = ptr::null_mut();
321
322 let mut dst = vec![0; val.len()];
323 encoding_rs::mem::convert_utf8_to_latin1_lossy(val.0.as_bytes(), dst.as_mut_slice());
324
325 check_status!(
326 unsafe {
327 sys::napi_create_string_latin1(env, dst.as_ptr() as *const _, dst.len(), &mut ptr)
328 },
329 "Failed to convert rust type `String` into napi `latin1 string`"
330 )?;
331
332 Ok(ptr)
333 }
334 }
335}
336
337unsafe extern "C" fn release_string(_env: sys::napi_env, data: *mut c_void, len: *mut c_void) {
338 let len: usize = unsafe { *Box::from_raw(len as *mut usize) };
339 unsafe { Vec::from_raw_parts(ptr:data as *mut u8, length:len, capacity:len) };
340}
341