Skip to main content

napi/bindgen_runtime/js_values/
string.rs

1use std::ffi::c_char;
2use std::fmt::Display;
3use std::ops::Deref;
4use std::ptr;
5
6use crate::{bindgen_prelude::*, check_status, check_status_and_type, sys};
7
8impl TypeName for String {
9  fn type_name() -> &'static str {
10    "String"
11  }
12
13  fn value_type() -> ValueType {
14    ValueType::String
15  }
16}
17
18impl ValidateNapiValue for String {}
19
20impl ToNapiValue for &String {
21  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
22    let mut ptr = ptr::null_mut();
23
24    check_status!(
25      unsafe {
26        sys::napi_create_string_utf8(env, val.as_ptr().cast(), val.len() as isize, &mut ptr)
27      },
28      "Failed to convert rust `String` into napi `string`"
29    )?;
30
31    Ok(ptr)
32  }
33}
34
35impl ToNapiValue for &mut String {
36  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
37    ToNapiValue::to_napi_value(env, &*val)
38  }
39}
40
41impl ToNapiValue for String {
42  #[inline]
43  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
44    #[allow(clippy::needless_borrows_for_generic_args)]
45    unsafe {
46      ToNapiValue::to_napi_value(env, &val)
47    }
48  }
49}
50
51impl FromNapiValue for String {
52  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
53    let mut len = 0;
54
55    check_status_and_type!(
56      unsafe { sys::napi_get_value_string_utf8(env, napi_val, ptr::null_mut(), 0, &mut len) },
57      env,
58      napi_val,
59      "Failed to convert JavaScript value `{}` into rust type `String`"
60    )?;
61
62    // end char len in C
63    len += 1;
64    let mut ret: Vec<u8> = vec![0; len];
65
66    let mut written_char_count = 0;
67
68    check_status_and_type!(
69      unsafe {
70        sys::napi_get_value_string_utf8(
71          env,
72          napi_val,
73          ret.as_mut_ptr().cast(),
74          len,
75          &mut written_char_count,
76        )
77      },
78      env,
79      napi_val,
80      "Failed to convert napi `{}` into rust type `String`"
81    )?;
82
83    ret.truncate(written_char_count);
84
85    Ok(unsafe { String::from_utf8_unchecked(ret) })
86  }
87}
88
89impl ToNapiValue for &str {
90  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
91    let mut ptr = ptr::null_mut();
92
93    check_status!(
94      unsafe {
95        sys::napi_create_string_utf8(env, val.as_ptr().cast(), val.len() as isize, &mut ptr)
96      },
97      "Failed to convert rust `&str` into napi `string`"
98    )?;
99
100    Ok(ptr)
101  }
102}
103
104#[derive(Debug)]
105pub struct Utf16String(Vec<u16>);
106
107impl ValidateNapiValue for Utf16String {}
108
109impl From<String> for Utf16String {
110  fn from(s: String) -> Self {
111    Utf16String(s.encode_utf16().collect())
112  }
113}
114
115impl From<Vec<u16>> for Utf16String {
116  fn from(data: Vec<u16>) -> Self {
117    Utf16String(data)
118  }
119}
120
121impl Display for Utf16String {
122  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123    write!(f, "{}", String::from_utf16_lossy(self))
124  }
125}
126
127impl Deref for Utf16String {
128  type Target = [u16];
129
130  fn deref(&self) -> &Self::Target {
131    self.0.as_ref()
132  }
133}
134
135impl TypeName for Utf16String {
136  fn type_name() -> &'static str {
137    "String(utf16)"
138  }
139
140  fn value_type() -> ValueType {
141    ValueType::String
142  }
143}
144
145impl FromNapiValue for Utf16String {
146  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
147    let mut len = 0;
148
149    check_status!(
150      unsafe { sys::napi_get_value_string_utf16(env, napi_val, ptr::null_mut(), 0, &mut len) },
151      "Failed to convert napi `utf16 string` into rust type `String`",
152    )?;
153
154    // end char len in C
155    len += 1;
156    let mut ret = vec![0; len];
157    let mut written_char_count = 0;
158
159    check_status!(
160      unsafe {
161        sys::napi_get_value_string_utf16(
162          env,
163          napi_val,
164          ret.as_mut_ptr(),
165          len,
166          &mut written_char_count,
167        )
168      },
169      "Failed to convert napi `utf16 string` into rust type `String`",
170    )?;
171
172    ret.truncate(written_char_count);
173
174    Ok(Utf16String(ret))
175  }
176}
177
178impl ToNapiValue for Utf16String {
179  unsafe fn to_napi_value(env: sys::napi_env, val: Utf16String) -> Result<sys::napi_value> {
180    let mut ptr = ptr::null_mut();
181
182    check_status!(
183      unsafe {
184        sys::napi_create_string_utf16(env, val.0.as_ptr().cast(), val.len() as isize, &mut ptr)
185      },
186      "Failed to convert napi `string` into rust type `String`"
187    )?;
188
189    Ok(ptr)
190  }
191}
192
193#[derive(Debug)]
194pub struct Latin1String(Vec<u8>);
195
196impl ValidateNapiValue for Latin1String {}
197
198impl From<String> for Latin1String {
199  fn from(s: String) -> Self {
200    Latin1String(s.into_bytes())
201  }
202}
203
204#[cfg(feature = "latin1")]
205impl Display for Latin1String {
206  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207    let mut dst_slice = vec![0; self.0.len() * 2];
208    let written =
209      encoding_rs::mem::convert_latin1_to_utf8(self.0.as_slice(), dst_slice.as_mut_slice());
210    dst_slice.truncate(written);
211    write!(f, "{}", unsafe { String::from_utf8_unchecked(dst_slice) })
212  }
213}
214
215impl Deref for Latin1String {
216  type Target = [u8];
217
218  fn deref(&self) -> &Self::Target {
219    self.0.as_slice()
220  }
221}
222
223impl TypeName for Latin1String {
224  fn type_name() -> &'static str {
225    "String(latin1)"
226  }
227
228  fn value_type() -> ValueType {
229    ValueType::String
230  }
231}
232
233impl FromNapiValue for Latin1String {
234  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
235    let mut len = 0;
236
237    check_status!(
238      unsafe { sys::napi_get_value_string_latin1(env, napi_val, ptr::null_mut(), 0, &mut len) },
239      "Failed to convert napi `latin1 string` into rust type `String`",
240    )?;
241
242    // end char len in C
243    len += 1;
244    let mut buf: Vec<u8> = vec![0; len];
245
246    let mut written_char_count = 0;
247
248    check_status!(
249      unsafe {
250        sys::napi_get_value_string_latin1(
251          env,
252          napi_val,
253          buf.as_mut_ptr().cast(),
254          len,
255          &mut written_char_count,
256        )
257      },
258      "Failed to convert napi `latin1 string` into rust type `String`"
259    )?;
260    buf.truncate(written_char_count);
261    Ok(Latin1String(buf))
262  }
263}
264
265impl ToNapiValue for Latin1String {
266  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
267    let mut ptr = ptr::null_mut();
268
269    check_status!(
270      unsafe {
271        sys::napi_create_string_latin1(env, val.0.as_ptr().cast(), val.len() as isize, &mut ptr)
272      },
273      "Failed to convert rust type `String` into napi `latin1 string`"
274    )?;
275
276    Ok(ptr)
277  }
278}
279
280pub const NAPI_AUTO_LENGTH: isize = -1;
281
282#[derive(Debug)]
283/// A wrapper around the raw c_char pointer to a C string.
284///
285/// This is useful when you want to return a C string to JavaScript directly via NAPI-RS function without converting it to Rust string or performing any memory allocation.
286///
287/// The `RawCString` doesn't implement `FromNapiValue`, so you can't convert a JavaScript String to it.
288pub struct RawCString {
289  length: isize,
290  inner: *const c_char,
291}
292
293impl RawCString {
294  /// Create a new `RawCString` from a raw pointer and length.
295  ///
296  /// If the inner string is null-terminated, you can pass `` as the length.
297  pub fn new(inner: *const c_char, length: isize) -> Self {
298    Self { inner, length }
299  }
300}
301
302impl ToNapiValue for RawCString {
303  unsafe fn to_napi_value(env: napi_sys::napi_env, val: Self) -> Result<napi_sys::napi_value> {
304    let mut ptr = ptr::null_mut();
305
306    check_status!(
307      napi_sys::napi_create_string_utf8(env, val.inner, val.length, &mut ptr),
308      "Failed to convert rust `&str` into napi `string`"
309    )?;
310
311    Ok(ptr)
312  }
313}