ducc/string.rs
1use cesu8::from_cesu8;
2use error::{Error, Result};
3use ffi;
4use std::slice;
5use std::string::String as StdString;
6use types::Ref;
7
8/// An immutable, interned JavaScript string managed by Duktape.
9///
10/// Unlike Rust strings, Duktape strings are CESU-8 (not UTF-8).
11#[derive(Clone, Debug)]
12pub struct String<'ducc>(pub(crate) Ref<'ducc>);
13
14impl<'ducc> String<'ducc> {
15 /// Returns a Rust string converted from the Duktape string as long as it can be converted from
16 /// CESU-8 to UTF-8.
17 pub fn to_string(&self) -> Result<StdString> {
18 match from_cesu8(self.as_bytes()) {
19 Ok(string) => Ok(string.into_owned()),
20 Err(_) => Err(Error::from_js_conversion("string", "String"))
21 }
22 }
23
24 /// Returns the bytes that make up this string, without a trailing nul byte. This is a CESU-8
25 /// string.
26 pub fn as_bytes(&self) -> &[u8] {
27 let with_nul = self.as_bytes_with_nul();
28 &with_nul[..with_nul.len() - 1]
29 }
30
31 /// Returns the bytes that make up this string, including a trailing nul byte. This is a CESU-8
32 /// string.
33 pub fn as_bytes_with_nul(&self) -> &[u8] {
34 // Strings are interned and cannot be modified, so the returned reference to its bytes is
35 // guaranteed to live as long as the reference lives.
36 unsafe {
37 let ducc = self.0.ducc;
38 let ctx = ducc.ctx;
39 assert_stack!(ctx, 0, {
40 ducc.push_ref(&self.0);
41 assert!(ffi::duk_is_string(ctx, -1) != 0);
42 let mut len = 0;
43 let data = ffi::duk_get_lstring(ctx, -1, &mut len);
44 assert!(!data.is_null());
45 let bytes = slice::from_raw_parts(data as *const u8, len + 1 as usize);
46 assert!(bytes[bytes.len() - 1] == 0);
47 ffi::duk_pop(ctx);
48 bytes
49 })
50 }
51 }
52}
53
54impl<'ducc> AsRef<[u8]> for String<'ducc> {
55 fn as_ref(&self) -> &[u8] {
56 self.as_bytes()
57 }
58}
59
60// Duktape strings are basically &[u8] slices, so implement PartialEq for anything resembling that.
61//
62// This makes our `String` comparable with `Vec<u8>`, `[u8]`, `&str`, `String`, and `ducc::String`
63// itself.
64//
65// The only downside is that this disallows a comparison with `Cow<str>`, as that only implements
66// `AsRef<str>`, which collides with this impl. Requiring `AsRef<str>` would fix that, but would
67// limit us in other ways.
68impl<'ducc, T: AsRef<[u8]>> PartialEq<T> for String<'ducc> {
69 fn eq(&self, other: &T) -> bool {
70 self.as_bytes() == other.as_ref()
71 }
72}