1 | use crate::{bindgen_prelude::*, check_status, check_status_and_type, sys, Error, Result, Status}; |
2 | |
3 | use std::ffi::{c_void, CStr}; |
4 | use std::fmt::Display; |
5 | use std::mem; |
6 | use std::ops::Deref; |
7 | use std::ptr; |
8 | |
9 | impl TypeName for String { |
10 | fn type_name() -> &'static str { |
11 | "String" |
12 | } |
13 | |
14 | fn value_type() -> ValueType { |
15 | ValueType::String |
16 | } |
17 | } |
18 | |
19 | impl ValidateNapiValue for String {} |
20 | |
21 | impl 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 | |
34 | impl 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 | |
44 | impl 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 | |
84 | impl TypeName for &str { |
85 | fn type_name() -> &'static str { |
86 | "String" |
87 | } |
88 | |
89 | fn value_type() -> ValueType { |
90 | ValueType::String |
91 | } |
92 | } |
93 | |
94 | impl ValidateNapiValue for &str {} |
95 | |
96 | impl 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 | |
150 | impl 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)] |
157 | pub struct Utf16String(String); |
158 | |
159 | impl ValidateNapiValue for Utf16String {} |
160 | |
161 | impl From<String> for Utf16String { |
162 | fn from(s: String) -> Self { |
163 | Utf16String(s) |
164 | } |
165 | } |
166 | |
167 | impl Display for Utf16String { |
168 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
169 | write!(f, " {}" , self.0) |
170 | } |
171 | } |
172 | |
173 | impl Deref for Utf16String { |
174 | type Target = str; |
175 | |
176 | fn deref(&self) -> &Self::Target { |
177 | self.0.as_ref() |
178 | } |
179 | } |
180 | |
181 | impl 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 | |
191 | impl 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 | |
230 | impl 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" )] |
248 | pub 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 | |
343 | unsafe 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 | |