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