Skip to content

Commit

Permalink
Merge pull request #714 from joriskleiber/add-gstring-chars
Browse files Browse the repository at this point in the history
Add `GString::chars` for 4.1
  • Loading branch information
Bromeon authored May 21, 2024
2 parents a9a57a5 + 9bba7b8 commit d093359
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 5 deletions.
34 changes: 33 additions & 1 deletion godot-core/src/builtin/string/gstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ impl GString {
///
/// Note: This operation is *O*(*n*). Consider using [`chars_unchecked`][Self::chars_unchecked]
/// if you can make sure the string is a valid UTF-32.
#[cfg_attr(
since_api = "4.1",
deprecated = "Use `chars()` instead. \n\
Since version 4.1, Godot ensures valid UTF-32, checked and unchecked overloads are no longer needed. \n\
For details, see [godotengine/godot#74760](https://github.com/godotengine/godot/pull/74760)."
)]
pub fn chars_checked(&self) -> &[char] {
unsafe {
let s = self.string_sys();
Expand All @@ -111,6 +117,12 @@ impl GString {
/// Make sure the string only contains valid unicode scalar values, currently
/// Godot allows for unpaired surrogates and out of range code points to be appended
/// into the string.
#[cfg_attr(
since_api = "4.1",
deprecated = "Use `chars()` instead. \n\
Since version 4.1, ensures valid UTF-32, checked and unchecked overloads are no longer needed. \n\
For details, see [godotengine/godot#74760](https://github.com/godotengine/godot/pull/74760)."
)]
pub unsafe fn chars_unchecked(&self) -> &[char] {
let s = self.string_sys();
let len = interface_fn!(string_to_utf32_chars)(s, std::ptr::null_mut(), 0);
Expand All @@ -123,6 +135,16 @@ impl GString {
std::slice::from_raw_parts(ptr as *const char, len as usize)
}

/// Gets the internal chars slice from a [`GString`].
#[cfg(since_api = "4.1")]
pub fn chars(&self) -> &[char] {
// SAFETY: Godot 4.1 ensures valid UTF-32, making this safe to call.
#[allow(deprecated)]
unsafe {
self.chars_unchecked()
}
}

ffi_methods! {
type sys::GDExtensionStringPtr = *mut Self;

Expand Down Expand Up @@ -181,7 +203,17 @@ impl_builtin_traits! {

impl fmt::Display for GString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s: String = self.chars_checked().iter().collect();
let s: String;

#[cfg(before_api = "4.1")]
{
s = self.chars_checked().iter().collect();
}
#[cfg(since_api = "4.1")]
{
s = self.chars().iter().collect();
}

f.write_str(s.as_str())
}
}
Expand Down
22 changes: 18 additions & 4 deletions itest/rust/src/builtin_tests/string/gstring_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,32 @@ fn string_clone() {
fn empty_string_chars() {
// Tests regression from #228: Null pointer passed to slice::from_raw_parts
let s = GString::new();
assert_eq!(s.chars_checked(), &[]);
assert_eq!(unsafe { s.chars_unchecked() }, &[]);

#[allow(deprecated)]
{
assert_eq!(s.chars_checked(), &[]);
assert_eq!(unsafe { s.chars_unchecked() }, &[]);
}
#[cfg(since_api = "4.1")]
{
assert_eq!(s.chars(), &[]);
}
}

#[itest]
fn string_chars() {
let string = String::from("some_string");
let string_chars: Vec<char> = string.chars().collect();
let gstring = GString::from(string);
let gstring_chars: Vec<char> = gstring.chars_checked().to_vec();

assert_eq!(gstring_chars, string_chars);
#[allow(deprecated)]
{
assert_eq!(string_chars, gstring.chars_checked().to_vec());
}
#[cfg(since_api = "4.1")]
{
assert_eq!(string_chars, gstring.chars().to_vec());
}
}

#[itest]
Expand Down

0 comments on commit d093359

Please sign in to comment.