use core::{hash::Hash, ptr::NonNull};
#[cfg(all(not(feature = "alloc"), feature = "std"))]
use std as alloc;
#[cfg(feature = "alloc")]
use alloc::{borrow::Cow, string::String};
use crate::Registry;
#[derive(Clone, Copy)]
#[repr(transparent)] pub struct Symbol(&'static &'static str);
impl Symbol {
#[inline]
#[must_use]
#[cfg(feature = "alloc")]
pub fn new(string: impl AsRef<str>) -> Symbol {
Self::new_(string.as_ref())
}
#[inline]
#[must_use]
#[cfg(feature = "alloc")]
fn new_(string: &str) -> Symbol {
Registry::global().get_or_insert(string)
}
#[inline]
#[must_use]
pub fn new_static(string: &'static &'static str) -> Symbol {
Registry::global().get_or_insert_static(string)
}
pub fn get(string: impl AsRef<str>) -> Option<Symbol> {
Self::get_(string.as_ref())
}
#[inline]
fn get_(string: &str) -> Option<Symbol> {
Registry::global().get(string)
}
#[inline]
#[must_use]
pub unsafe fn new_unchecked(registered_symbol: &'static &'static str) -> Symbol {
Symbol(registered_symbol)
}
#[inline]
#[must_use]
pub const fn as_str(&self) -> &'static str {
self.0
}
#[inline]
#[must_use]
pub const fn inner(&self) -> &'static &'static str {
self.0
}
#[inline]
#[must_use]
pub const fn as_ptr(&self) -> NonNull<&'static str> {
unsafe { NonNull::new_unchecked(core::ptr::from_ref::<&'static str>(self.0) as *mut _) }
}
#[inline]
#[must_use]
pub fn to_ffi(&self) -> u64 {
self.as_ptr().as_ptr() as usize as u64
}
#[inline]
#[must_use]
#[allow(clippy::cast_possible_truncation)] pub unsafe fn from_ffi(value: u64) -> Symbol {
unsafe { Self::new_unchecked(&*(value as usize as *const &'static str)) }
}
#[inline]
#[must_use]
pub fn try_from_ffi(value: u64) -> Option<Symbol> {
Registry::global().get_by_address(value)
}
#[inline]
#[must_use]
pub const fn len(&self) -> usize {
self.0.len()
}
#[inline]
#[must_use]
pub const fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl PartialEq for Symbol {
#[inline]
fn eq(&self, other: &Self) -> bool {
self.as_ptr() == other.as_ptr()
}
}
impl Eq for Symbol {}
impl PartialEq<str> for Symbol {
#[inline]
fn eq(&self, other: &str) -> bool {
*self.as_str() == *other
}
}
impl PartialEq<&str> for Symbol {
#[inline]
fn eq(&self, other: &&str) -> bool {
*self.as_str() == **other
}
}
impl PartialEq<Symbol> for str {
#[inline]
fn eq(&self, other: &Symbol) -> bool {
*self == *other.as_str()
}
}
impl PartialEq<Symbol> for &str {
#[inline]
fn eq(&self, other: &Symbol) -> bool {
**self == *other.as_str()
}
}
impl PartialOrd for Symbol {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Symbol {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.as_ptr().cmp(&other.as_ptr())
}
}
impl PartialOrd<str> for Symbol {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<core::cmp::Ordering> {
Some(self.as_str().cmp(other))
}
}
impl PartialOrd<&str> for Symbol {
#[inline]
fn partial_cmp(&self, other: &&str) -> Option<core::cmp::Ordering> {
Some(self.as_str().cmp(*other))
}
}
impl PartialOrd<Symbol> for str {
#[inline]
fn partial_cmp(&self, other: &Symbol) -> Option<core::cmp::Ordering> {
Some(self.cmp(other.as_str()))
}
}
impl PartialOrd<Symbol> for &str {
#[inline]
fn partial_cmp(&self, other: &Symbol) -> Option<core::cmp::Ordering> {
Some((*self).cmp(other.as_str()))
}
}
impl Hash for Symbol {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.as_ptr().hash(state);
}
}
impl AsRef<str> for Symbol {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
#[cfg(feature = "alloc")]
impl From<&str> for Symbol {
#[inline]
fn from(value: &str) -> Self {
Symbol::new(value)
}
}
#[cfg(feature = "alloc")]
impl From<String> for Symbol {
#[inline]
fn from(value: String) -> Self {
Symbol::new(&*value)
}
}
#[cfg(feature = "alloc")]
impl<'a> From<Cow<'a, str>> for Symbol {
fn from(value: Cow<'a, str>) -> Self {
Symbol::new(&*value)
}
}
impl core::fmt::Display for Symbol {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Display::fmt(self.as_str(), f)
}
}
impl core::fmt::Debug for Symbol {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
core::fmt::Debug::fmt(self.as_str(), f)
}
}
#[cfg(feature = "serde")]
const _: () = {
impl serde::Serialize for Symbol {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_str().serialize(serializer)
}
}
#[cfg(feature = "alloc")]
impl<'de> serde::Deserialize<'de> for Symbol {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = Cow::<'de, str>::deserialize(deserializer)?;
Ok(Symbol::new(&*s))
}
}
#[cfg(not(feature = "alloc"))]
impl<'de> serde::Deserialize<'de> for Symbol {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = <&'de str>::deserialize(deserializer)?;
Ok(Symbol::new(&*s))
}
}
};