use std::fmt;
use godot_ffi as sys;
use godot_ffi::{ExtVariantType, GdextBuild, GodotFfi, ffi_methods};
use super::{GString, StringName};
use crate::builtin::inner;
use crate::meta::signed_range::SignedRange;
pub struct NodePath {
opaque: sys::types::OpaqueNodePath,
}
impl NodePath {
fn from_opaque(opaque: sys::types::OpaqueNodePath) -> Self {
Self { opaque }
}
pub fn get_name(&self, index: usize) -> StringName {
let inner = self.as_inner();
let index = index as i64;
sys::balanced_assert!(
index < inner.get_name_count(),
"NodePath '{self}': name at index {index} is out of bounds"
);
inner.get_name(index)
}
pub fn get_subname(&self, index: usize) -> StringName {
let inner = self.as_inner();
let index = index as i64;
sys::balanced_assert!(
index < inner.get_subname_count(),
"NodePath '{self}': subname at index {index} is out of bounds"
);
inner.get_subname(index)
}
pub fn get_name_count(&self) -> usize {
self.as_inner()
.get_name_count()
.try_into()
.expect("Godot name counts are non-negative ints")
}
pub fn get_subname_count(&self) -> usize {
self.as_inner()
.get_subname_count()
.try_into()
.expect("Godot subname counts are non-negative ints")
}
pub fn get_total_count(&self) -> usize {
self.get_name_count() + self.get_subname_count()
}
crate::declare_hash_u32_method! {
}
#[cfg(since_api = "4.3")] #[cfg_attr(published_docs, doc(cfg(since_api = "4.3")))]
#[doc(alias = "slice")]
pub fn subpath(&self, range: impl SignedRange) -> NodePath {
let (from, exclusive_end) = range.signed();
let begin = if GdextBuild::since_api("4.4") {
from
} else {
let name_count = self.get_name_count() as i64;
let subname_count = self.get_subname_count() as i64;
let total_count = name_count + subname_count;
let mut begin = from.clamp(-total_count, total_count);
if begin < 0 {
begin += total_count;
}
if begin > name_count {
begin += 1;
}
begin
};
self.as_inner()
.slice(begin, exclusive_end.unwrap_or(i32::MAX as i64))
}
#[doc(hidden)]
pub fn as_inner(&self) -> inner::InnerNodePath<'_> {
inner::InnerNodePath::from_outer(self)
}
}
unsafe impl GodotFfi for NodePath {
const VARIANT_TYPE: ExtVariantType = ExtVariantType::Concrete(sys::VariantType::NODE_PATH);
ffi_methods! { type sys::GDExtensionTypePtr = *mut Opaque; .. }
}
crate::meta::impl_godot_as_self!(NodePath: ByRef);
impl_builtin_traits! {
for NodePath {
Default => node_path_construct_default;
Clone => node_path_construct_copy;
Drop => node_path_destroy;
Eq => node_path_operator_equal;
Hash;
}
}
impl fmt::Display for NodePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let string = GString::from(self);
<GString as fmt::Display>::fmt(&string, f)
}
}
impl fmt::Debug for NodePath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let string = GString::from(self);
write!(f, "^\"{string}\"")
}
}
impl_rust_string_conv!(NodePath);
impl From<&str> for NodePath {
fn from(s: &str) -> Self {
Self::from(&GString::from(s))
}
}
impl From<&String> for NodePath {
fn from(s: &String) -> Self {
Self::from(&GString::from(s))
}
}
impl From<&GString> for NodePath {
fn from(string: &GString) -> Self {
unsafe {
Self::new_with_uninit(|self_ptr| {
let ctor = sys::builtin_fn!(node_path_from_string);
let args = [string.sys()];
ctor(self_ptr, args.as_ptr());
})
}
}
}
impl From<&StringName> for NodePath {
fn from(s: &StringName) -> Self {
Self::from(&GString::from(s))
}
}
#[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 NodePath {
#[inline]
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
serializer.serialize_newtype_struct("NodePath", &*self.to_string())
}
}
#[cfg_attr(published_docs, doc(cfg(feature = "serde")))]
impl<'de> Deserialize<'de> for NodePath {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct NodePathVisitor;
impl<'de> Visitor<'de> for NodePathVisitor {
type Value = NodePath;
fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a NodePath")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(NodePath::from(s))
}
fn visit_newtype_struct<D>(
self,
deserializer: D,
) -> Result<Self::Value, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(self)
}
}
deserializer.deserialize_newtype_struct("NodePath", NodePathVisitor)
}
}
}