ul_next/javascript/
string.rs

1use core::fmt;
2use std::{ffi::c_char, sync::Arc};
3
4use crate::Library;
5
6/// A JavaScript string.
7///
8/// Its internally a UTF16 character buffer.
9pub struct JSString {
10    pub(crate) internal: ul_sys::JSStringRef,
11    lib: Arc<Library>,
12}
13
14impl JSString {
15    pub(crate) fn from_raw(lib: Arc<Library>, string: ul_sys::JSStringRef) -> Self {
16        assert!(!string.is_null());
17
18        Self {
19            internal: string,
20            lib,
21        }
22    }
23
24    pub(crate) fn copy_from_raw(
25        lib: Arc<Library>,
26        string: *mut ul_sys::OpaqueJSString,
27    ) -> JSString {
28        assert!(!string.is_null());
29
30        let string = unsafe { lib.ultralight().JSStringRetain(string) };
31
32        Self {
33            internal: string,
34            lib,
35        }
36    }
37
38    /// Creates a new JavaScript string from a Rust string.
39    pub fn new(lib: Arc<Library>, string: &str) -> Self {
40        let cstring = std::ffi::CString::new(string).unwrap();
41
42        let string = unsafe {
43            lib.ultralight()
44                .JSStringCreateWithUTF8CString(cstring.as_ptr())
45        };
46
47        Self {
48            internal: string,
49            lib,
50        }
51    }
52
53    /// Returns `true` if the string is empty.
54    pub fn is_empty(&self) -> bool {
55        self.len() == 0
56    }
57
58    /// Returns the number of Unicode characters in the JavaScript string.
59    pub fn len(&self) -> usize {
60        unsafe { self.lib.ultralight().JSStringGetLength(self.internal) }
61    }
62}
63
64impl From<&JSString> for String {
65    fn from(string: &JSString) -> Self {
66        let max_size = unsafe {
67            string
68                .lib
69                .ultralight()
70                .JSStringGetMaximumUTF8CStringSize(string.internal)
71        };
72
73        let mut buffer: Vec<u8> = Vec::with_capacity(max_size);
74
75        unsafe {
76            let actual_size = string.lib.ultralight().JSStringGetUTF8CString(
77                string.internal,
78                buffer.as_mut_ptr().cast::<c_char>(),
79                max_size,
80            );
81            buffer.set_len(actual_size - 1);
82        }
83
84        String::from_utf8(buffer).unwrap()
85    }
86}
87
88impl Clone for JSString {
89    fn clone(&self) -> Self {
90        let string = unsafe { self.lib.ultralight().JSStringRetain(self.internal) };
91
92        Self {
93            internal: string,
94            lib: self.lib.clone(),
95        }
96    }
97}
98
99impl fmt::Debug for JSString {
100    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
101        String::from(self).fmt(f)
102    }
103}
104
105impl fmt::Display for JSString {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        String::from(self).fmt(f)
108    }
109}
110
111impl Drop for JSString {
112    fn drop(&mut self) {
113        unsafe {
114            self.lib.ultralight().JSStringRelease(self.internal);
115        }
116    }
117}