use std::convert::Infallible;
use std::fmt;
use std::fmt::Write;
use godot_ffi as sys;
use sys::{ExtVariantType, GodotFfi, ffi_methods, interface_fn};
use crate::builtin::strings::{Encoding, pad_if_needed};
use crate::builtin::{NodePath, StringName, Variant, inner};
use crate::meta::AsArg;
use crate::meta::error::StringError;
use crate::{impl_shared_string_api, meta};
#[doc(alias = "String")]
#[repr(transparent)]
pub struct GString {
_opaque: sys::types::OpaqueString,
}
unsafe impl Send for GString {}
impl GString {
pub fn new() -> Self {
Self::default()
}
pub fn try_from_bytes(bytes: &[u8], encoding: Encoding) -> Result<Self, StringError> {
Self::try_from_bytes_with_nul_check(bytes, encoding, true)
}
pub fn try_from_cstr(cstr: &std::ffi::CStr, encoding: Encoding) -> Result<Self, StringError> {
Self::try_from_bytes_with_nul_check(cstr.to_bytes(), encoding, false)
}
pub(super) fn try_from_bytes_with_nul_check(
bytes: &[u8],
encoding: Encoding,
check_nul: bool,
) -> Result<Self, StringError> {
match encoding {
Encoding::Ascii => {
if bytes.is_ascii() {
Self::try_from_bytes_with_nul_check(bytes, Encoding::Latin1, check_nul)
.map_err(|_e| StringError::new("intermediate NUL byte in ASCII string"))
} else {
Err(StringError::new("invalid ASCII"))
}
}
Encoding::Latin1 => {
if check_nul && bytes.contains(&0) {
return Err(StringError::new("intermediate NUL byte in Latin-1 string"));
}
let s = unsafe {
Self::new_with_string_uninit(|string_ptr| {
let ctor = interface_fn!(string_new_with_latin1_chars_and_len);
ctor(
string_ptr,
bytes.as_ptr() as *const std::ffi::c_char,
bytes.len() as i64,
);
})
};
Ok(s)
}
Encoding::Utf8 => {
let utf8 = std::str::from_utf8(bytes);
utf8.map(GString::from)
.map_err(|e| StringError::with_source("invalid UTF-8", e))
}
}
}
#[doc(alias = "length")]
pub fn len(&self) -> usize {
self.as_inner().length().try_into().unwrap()
}
crate::declare_hash_u32_method! {
}
pub fn chars(&self) -> &[char] {
let (ptr, len) = self.raw_slice();
if ptr.is_null() {
return &[];
}
unsafe { std::slice::from_raw_parts(ptr, len) }
}
pub(crate) fn raw_slice(&self) -> (*const char, usize) {
let s = self.string_sys();
let len: sys::GDExtensionInt;
let ptr: *const sys::char32_t;
unsafe {
len = interface_fn!(string_to_utf32_chars)(s, std::ptr::null_mut(), 0);
ptr = interface_fn!(string_operator_index_const)(s, 0);
}
(ptr.cast(), len as usize)
}
ffi_methods! {
type sys::GDExtensionStringPtr = *mut Self;
fn new_from_string_sys = new_from_sys;
fn new_with_string_uninit = new_with_uninit;
fn string_sys = sys;
fn string_sys_mut = sys_mut;
}
pub(crate) fn into_owned_string_sys(self) -> sys::GDExtensionStringPtr {
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueString);
let leaked = Box::into_raw(Box::new(self));
leaked.cast()
}
pub(crate) unsafe fn from_owned_string_sys(ptr: sys::GDExtensionStringPtr) -> Self {
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueString);
let ptr = ptr.cast::<Self>();
let boxed = unsafe { Box::from_raw(ptr) };
*boxed
}
pub(crate) unsafe fn borrow_string_sys_mut<'a>(ptr: sys::GDExtensionStringPtr) -> &'a mut Self {
sys::static_assert_eq_size_align!(StringName, sys::types::OpaqueString);
unsafe { &mut *(ptr.cast::<GString>()) }
}
pub(crate) unsafe fn move_into_string_ptr(self, dst: sys::GDExtensionStringPtr) {
let dst: sys::GDExtensionTypePtr = dst.cast();
unsafe { self.move_return_ptr(dst, sys::PtrcallType::Standard) };
}
#[doc(hidden)]
pub fn as_inner(&self) -> inner::InnerString<'_> {
inner::InnerString::from_outer(self)
}
}
unsafe impl GodotFfi for GString {
const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::STRING);
ffi_methods! { type sys::GDExtensionTypePtr = *mut Self; .. }
}
meta::impl_godot_as_self!(GString: ByRef);
impl_builtin_traits! {
for GString {
Default => string_construct_default;
Clone => string_construct_copy;
Drop => string_destroy;
Eq => string_operator_equal;
Ord => string_operator_less;
Hash;
}
}
impl_shared_string_api! {
builtin: GString,
builtin_mod: gstring,
}
impl fmt::Display for GString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
pad_if_needed(f, |f| {
for ch in self.chars() {
f.write_char(*ch)?;
}
Ok(())
})
}
}
impl fmt::Debug for GString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "\"{self}\"")
}
}
impl PartialEq<&str> for GString {
fn eq(&self, other: &&str) -> bool {
self.chars().iter().copied().eq(other.chars())
}
}
impl From<&str> for GString {
fn from(s: &str) -> Self {
let bytes = s.as_bytes();
unsafe {
Self::new_with_string_uninit(|string_ptr| {
#[cfg(before_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(before_api = "4.3")))]
let ctor = interface_fn!(string_new_with_utf8_chars_and_len);
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
let ctor = interface_fn!(string_new_with_utf8_chars_and_len2);
ctor(
string_ptr,
bytes.as_ptr() as *const std::ffi::c_char,
bytes.len() as i64,
);
})
}
}
}
impl From<&[char]> for GString {
fn from(chars: &[char]) -> Self {
unsafe {
Self::new_with_string_uninit(|string_ptr| {
let ctor = interface_fn!(string_new_with_utf32_chars_and_len);
ctor(
string_ptr,
chars.as_ptr() as *const sys::char32_t,
chars.len() as i64,
);
})
}
}
}
impl From<&String> for GString {
fn from(value: &String) -> Self {
value.as_str().into()
}
}
impl From<&GString> for String {
fn from(string: &GString) -> Self {
unsafe {
let len =
interface_fn!(string_to_utf8_chars)(string.string_sys(), std::ptr::null_mut(), 0);
assert!(len >= 0);
let mut buf = vec![0u8; len as usize];
interface_fn!(string_to_utf8_chars)(
string.string_sys(),
buf.as_mut_ptr() as *mut std::ffi::c_char,
len,
);
String::from_utf8(buf).expect("String::from_utf8")
}
}
}
impl From<GString> for String {
fn from(string: GString) -> Self {
Self::from(&string)
}
}
impl std::str::FromStr for GString {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self::from(s))
}
}
impl From<&StringName> for GString {
fn from(string: &StringName) -> Self {
unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(string_from_string_name);
let args = [string.sys()];
ctor(self_ptr, args.as_ptr());
})
}
}
}
impl From<&NodePath> for GString {
fn from(path: &NodePath) -> Self {
unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(string_from_node_path);
let args = [path.sys()];
ctor(self_ptr, args.as_ptr());
})
}
}
}
#[cfg(feature = "serde")] #[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
mod serialize {
use std::fmt::Formatter;
use serde::de::{Error, Visitor};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::*;
#[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
impl Serialize for GString {
#[inline]
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
#[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
impl<'de> Deserialize<'de> for GString {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
struct GStringVisitor;
impl Visitor<'_> for GStringVisitor {
type Value = GString;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a GString")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(GString::from(s))
}
}
deserializer.deserialize_str(GStringVisitor)
}
}
}