rquickjs_core/value/
string.rs1use crate::{qjs, Ctx, Error, Result, StdString, Value};
2use std::{ffi::c_char, mem, ptr::NonNull, slice, str};
3
4#[derive(Debug, Clone, PartialEq, Hash)]
6#[repr(transparent)]
7pub struct String<'js>(pub(crate) Value<'js>);
8
9impl<'js> String<'js> {
10 pub fn to_string(&self) -> Result<StdString> {
12 let mut len = mem::MaybeUninit::uninit();
13 let ptr = unsafe {
14 qjs::JS_ToCStringLen(self.0.ctx.as_ptr(), len.as_mut_ptr(), self.0.as_js_value())
15 };
16 if ptr.is_null() {
17 return Err(Error::Unknown);
20 }
21 let len = unsafe { len.assume_init() };
22 let bytes: &[u8] = unsafe { slice::from_raw_parts(ptr as _, len as _) };
23 let result = str::from_utf8(bytes).map(|s| s.into());
24 unsafe { qjs::JS_FreeCString(self.0.ctx.as_ptr(), ptr) };
25 Ok(result?)
26 }
27
28 pub fn to_cstring(self) -> Result<CString<'js>> {
30 CString::from_string(self)
31 }
32
33 pub fn from_str(ctx: Ctx<'js>, s: &str) -> Result<Self> {
35 let len = s.as_bytes().len();
36 let ptr = s.as_ptr();
37 Ok(unsafe {
38 let js_val = qjs::JS_NewStringLen(ctx.as_ptr(), ptr as _, len as _);
39 let js_val = ctx.handle_exception(js_val)?;
40 String::from_js_value(ctx, js_val)
41 })
42 }
43}
44
45#[derive(Debug)]
47pub struct CString<'js> {
48 ptr: NonNull<c_char>,
49 len: usize,
50 ctx: Ctx<'js>,
51}
52
53impl<'js> CString<'js> {
54 pub fn from_string(string: String<'js>) -> Result<Self> {
56 let mut len = mem::MaybeUninit::uninit();
57 let ptr = unsafe {
59 qjs::JS_ToCStringLen(string.0.ctx.as_ptr(), len.as_mut_ptr(), string.as_raw())
60 };
61 if ptr.is_null() {
62 return Err(Error::Unknown);
65 }
66 let len = unsafe { len.assume_init() };
67 Ok(Self {
68 ptr: unsafe { NonNull::new_unchecked(ptr as *mut _) },
69 len,
70 ctx: string.0.ctx.clone(),
71 })
72 }
73
74 pub fn as_ptr(&self) -> *const c_char {
76 self.ptr.as_ptr() as *const _
77 }
78
79 pub fn len(&self) -> usize {
81 self.len
82 }
83
84 pub fn is_empty(&self) -> bool {
86 self.len == 0
87 }
88
89 pub fn as_str(&self) -> &str {
91 let bytes = unsafe { slice::from_raw_parts(self.ptr.as_ptr() as *const u8, self.len) };
93 unsafe { str::from_utf8_unchecked(bytes) }
95 }
96}
97
98impl<'js> Drop for CString<'js> {
99 fn drop(&mut self) {
100 unsafe { qjs::JS_FreeCString(self.ctx.as_ptr(), self.ptr.as_ptr()) };
101 }
102}
103
104#[cfg(test)]
105mod test {
106 use crate::{prelude::*, *};
107 #[test]
108 fn from_javascript() {
109 test_with(|ctx| {
110 let s: String = ctx.eval(" 'foo bar baz' ").unwrap();
111 assert_eq!(s.to_string().unwrap(), "foo bar baz");
112 });
113 }
114
115 #[test]
116 fn to_javascript() {
117 test_with(|ctx| {
118 let string = String::from_str(ctx.clone(), "foo").unwrap();
119 let func: Function = ctx.eval("x => x + 'bar'").unwrap();
120 let text: StdString = (string,).apply(&func).unwrap();
121 assert_eq!(text, "foobar".to_string());
122 });
123 }
124
125 #[test]
126 fn from_javascript_c() {
127 test_with(|ctx| {
128 let s: CString = ctx.eval(" 'foo bar baz' ").unwrap();
129 assert_eq!(s.as_str(), "foo bar baz");
130 });
131 }
132
133 #[test]
134 fn to_javascript_c() {
135 test_with(|ctx| {
136 let string = String::from_str(ctx.clone(), "foo")
137 .unwrap()
138 .to_cstring()
139 .unwrap();
140 let func: Function = ctx.eval("x => x + 'bar'").unwrap();
141 let text: StdString = (string,).apply(&func).unwrap();
142 assert_eq!(text, "foobar".to_string());
143 });
144 }
145}