use std::{
convert::TryFrom,
ffi::CStr,
fmt,
};
use crate::{
object::{NonNullObject, Ty},
prelude::*,
string::Encoding,
ruby,
};
#[derive(Clone, Copy, Debug)]
#[repr(transparent)]
pub struct Symbol(NonNullObject);
impl AsRef<AnyObject> for Symbol {
#[inline]
fn as_ref(&self) -> &AnyObject { self.0.as_ref() }
}
impl From<Symbol> for AnyObject {
#[inline]
fn from(object: Symbol) -> AnyObject { object.0.into() }
}
impl PartialEq<AnyObject> for Symbol {
#[inline]
fn eq(&self, obj: &AnyObject) -> bool {
self.as_any_object() == obj
}
}
unsafe impl Object for Symbol {
#[inline]
fn cast<A: Object>(obj: A) -> Option<Self> {
if obj.is_ty(Ty::SYMBOL) {
unsafe { Some(Self::cast_unchecked(obj)) }
} else {
None
}
}
#[inline]
fn ty(self) -> Ty { Ty::SYMBOL }
#[inline]
fn is_ty(self, ty: Ty) -> bool { ty == Ty::SYMBOL }
}
impl fmt::Display for Symbol {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.as_any_object().fmt(f)
}
}
impl From<String> for Symbol {
#[inline]
fn from(s: String) -> Symbol {
SymbolId::from(s).into()
}
}
impl From<&str> for Symbol {
#[inline]
fn from(s: &str) -> Symbol {
SymbolId::from(s).into()
}
}
impl TryFrom<Symbol> for &str {
type Error = std::str::Utf8Error;
#[inline]
fn try_from(s: Symbol) -> Result<Self, Self::Error> {
s.name().to_str()
}
}
impl TryFrom<Symbol> for std::string::String {
type Error = std::str::Utf8Error;
#[inline]
fn try_from(s: Symbol) -> Result<Self, Self::Error> {
s.name().to_str().map(Into::into)
}
}
impl Symbol {
#[inline]
pub(crate) fn _id(self) -> ruby::ID {
unsafe { ruby::rb_sym2id(self.raw()) }
}
#[inline]
pub fn all() -> Array<Self> {
unsafe { Array::from_raw(ruby::rb_sym_all_symbols()) }
}
#[inline]
pub fn global_vars() -> Array<Self> {
unsafe { Array::from_raw(ruby::rb_f_global_variables()) }
}
#[inline]
pub fn is_valid(name: impl AsRef<[u8]>) -> bool {
let name = name.as_ref();
let ptr = name.as_ptr();
let len = name.len();
let enc = Encoding::utf8()._enc();
unsafe { ruby::rb_enc_symname2_p(ptr as _, len as _, enc) != 0 }
}
#[inline]
pub fn id(self) -> SymbolId {
SymbolId(self._id())
}
#[inline]
pub fn name(self) -> &'static CStr {
self.id().name()
}
}
#[derive(Clone, Copy)]
pub struct SymbolId(ruby::ID);
impl fmt::Debug for SymbolId {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_tuple("SymbolId")
.field(&self.raw())
.finish()
}
}
impl From<String> for SymbolId {
#[inline]
fn from(s: String) -> SymbolId {
unsafe { SymbolId(ruby::rb_intern_str(s.raw())) }
}
}
impl From<&str> for SymbolId {
#[inline]
fn from(s: &str) -> SymbolId {
let raw = unsafe { ruby::rb_intern3(
s.as_ptr() as _,
s.len() as _,
Encoding::utf8()._enc(),
) };
SymbolId(raw)
}
}
impl From<Symbol> for SymbolId {
#[inline]
fn from(s: Symbol) -> Self {
SymbolId(s._id())
}
}
impl From<SymbolId> for Symbol {
#[inline]
fn from(id: SymbolId) -> Symbol {
unsafe { Symbol::from_raw(ruby::rb_id2sym(id.raw())) }
}
}
impl SymbolId {
#[inline]
pub fn raw(self) -> ruby::ID {
self.0
}
#[inline]
pub fn name(self) -> &'static CStr {
unsafe { CStr::from_ptr(ruby::rb_id2name(self.raw())) }
}
}