1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use std::{char, iter, slice};

use jni_sys::*;

use crate::Env;

/// Represents an env.GetStringChars + env.GetStringLength query.
/// Will automatically env.ReleaseStringChars when dropped.
pub struct StringChars<'env> {
    env: Env<'env>,
    string: jstring,
    chars: *const jchar,
    length: jsize, // in characters
}

impl<'env> StringChars<'env> {
    /// Construct a StringChars from an Env + jstring.
    pub unsafe fn from_env_jstring(env: Env<'env>, string: jstring) -> Self {
        debug_assert!(!string.is_null());

        let chars = env.get_string_chars(string);
        let length = env.get_string_length(string);

        Self {
            env,
            string,
            chars,
            length,
        }
    }

    /// Get an array of [jchar]s.  Generally UTF16, but not guaranteed to be valid UTF16.
    pub fn chars(&self) -> &[jchar] {
        unsafe { slice::from_raw_parts(self.chars, self.length as usize) }
    }

    /// [std::char::decode_utf16]\(...\)s these string characters.
    pub fn decode(&self) -> char::DecodeUtf16<iter::Cloned<slice::Iter<u16>>> {
        char::decode_utf16(self.chars().iter().cloned())
    }

    /// Returns a new [Ok]\([String]\), or an [Err]\([DecodeUtf16Error](char::DecodeUtf16Error)\) if if it contained any invalid UTF16.
    pub fn to_string(&self) -> Result<String, char::DecodeUtf16Error> {
        self.decode().collect()
    }

    /// Returns a new [String] with any invalid UTF16 characters replaced with [REPLACEMENT_CHARACTER](char::REPLACEMENT_CHARACTER)s (`'\u{FFFD}'`.)
    pub fn to_string_lossy(&self) -> String {
        self.decode()
            .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
            .collect()
    }
}

impl<'env> Drop for StringChars<'env> {
    fn drop(&mut self) {
        unsafe { self.env.release_string_chars(self.string, self.chars) };
    }
}