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 Display for Utf16String {
116  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117    write!(f, "{}", String::from_utf16_lossy(self))
118  }
119}
120
121impl Deref for Utf16String {
122  type Target = [u16];
123
124  fn deref(&self) -> &Self::Target {
125    self.0.as_ref()
126  }
127}
128
129impl TypeName for Utf16String {
130  fn type_name() -> &'static str {
131    "String(utf16)"
132  }
133
134  fn value_type() -> ValueType {
135    ValueType::String
136  }
137}
138
139impl FromNapiValue for Utf16String {
140  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
141    let mut len = 0;
142
143    check_status!(
144      unsafe { sys::napi_get_value_string_utf16(env, napi_val, ptr::null_mut(), 0, &mut len) },
145      "Failed to convert napi `utf16 string` into rust type `String`",
146    )?;
147
148    // end char len in C
149    len += 1;
150    let mut ret = vec![0; len];
151    let mut written_char_count = 0;
152
153    check_status!(
154      unsafe {
155        sys::napi_get_value_string_utf16(
156          env,
157          napi_val,
158          ret.as_mut_ptr(),
159          len,
160          &mut written_char_count,
161        )
162      },
163      "Failed to convert napi `utf16 string` into rust type `String`",
164    )?;
165
166    ret.truncate(written_char_count);
167
168    Ok(Utf16String(ret))
169  }
170}
171
172impl ToNapiValue for Utf16String {
173  unsafe fn to_napi_value(env: sys::napi_env, val: Utf16String) -> Result<sys::napi_value> {
174    let mut ptr = ptr::null_mut();
175
176    check_status!(
177      unsafe {
178        sys::napi_create_string_utf16(env, val.0.as_ptr().cast(), val.len() as isize, &mut ptr)
179      },
180      "Failed to convert napi `string` into rust type `String`"
181    )?;
182
183    Ok(ptr)
184  }
185}
186
187#[derive(Debug)]
188pub struct Latin1String(Vec<u8>);
189
190impl ValidateNapiValue for Latin1String {}
191
192impl From<String> for Latin1String {
193  fn from(s: String) -> Self {
194    Latin1String(s.into_bytes())
195  }
196}
197
198#[cfg(feature = "latin1")]
199impl Display for Latin1String {
200  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201    let mut dst_slice = vec![0; self.0.len() * 2];
202    let written =
203      encoding_rs::mem::convert_latin1_to_utf8(self.0.as_slice(), dst_slice.as_mut_slice());
204    dst_slice.truncate(written);
205    write!(f, "{}", unsafe { String::from_utf8_unchecked(dst_slice) })
206  }
207}
208
209impl Deref for Latin1String {
210  type Target = [u8];
211
212  fn deref(&self) -> &Self::Target {
213    self.0.as_slice()
214  }
215}
216
217impl TypeName for Latin1String {
218  fn type_name() -> &'static str {
219    "String(latin1)"
220  }
221
222  fn value_type() -> ValueType {
223    ValueType::String
224  }
225}
226
227impl FromNapiValue for Latin1String {
228  unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result<Self> {
229    let mut len = 0;
230
231    check_status!(
232      unsafe { sys::napi_get_value_string_latin1(env, napi_val, ptr::null_mut(), 0, &mut len) },
233      "Failed to convert napi `latin1 string` into rust type `String`",
234    )?;
235
236    // end char len in C
237    len += 1;
238    let mut buf: Vec<u8> = vec![0; len];
239
240    let mut written_char_count = 0;
241
242    check_status!(
243      unsafe {
244        sys::napi_get_value_string_latin1(
245          env,
246          napi_val,
247          buf.as_mut_ptr().cast(),
248          len,
249          &mut written_char_count,
250        )
251      },
252      "Failed to convert napi `latin1 string` into rust type `String`"
253    )?;
254    buf.truncate(written_char_count);
255    Ok(Latin1String(buf))
256  }
257}
258
259impl ToNapiValue for Latin1String {
260  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
261    let mut ptr = ptr::null_mut();
262
263    check_status!(
264      unsafe {
265        sys::napi_create_string_latin1(env, val.0.as_ptr().cast(), val.len() as isize, &mut ptr)
266      },
267      "Failed to convert rust type `String` into napi `latin1 string`"
268    )?;
269
270    Ok(ptr)
271  }
272}
273
274pub const NAPI_AUTO_LENGTH: isize = -1;
275
276#[derive(Debug)]
277/// A wrapper around the raw c_char pointer to a C string.
278///
279/// 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.
280///
281/// The `RawCString` doesn't implement `FromNapiValue`, so you can't convert a JavaScript String to it.
282pub struct RawCString {
283  length: isize,
284  inner: *const c_char,
285}
286
287impl RawCString {
288  /// Create a new `RawCString` from a raw pointer and length.
289  ///
290  /// If the inner string is null-terminated, you can pass `` as the length.
291  pub fn new(inner: *const c_char, length: isize) -> Self {
292    Self { inner, length }
293  }
294}
295
296impl ToNapiValue for RawCString {
297  unsafe fn to_napi_value(env: napi_sys::napi_env, val: Self) -> Result<napi_sys::napi_value> {
298    let mut ptr = ptr::null_mut();
299
300    check_status!(
301      napi_sys::napi_create_string_utf8(env, val.inner, val.length, &mut ptr),
302      "Failed to convert rust `&str` into napi `string`"
303    )?;
304
305    Ok(ptr)
306  }
307}