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}