use core::{cmp::Ordering, convert::Infallible, fmt::Debug};
use super::{
env::internal::{Env as _, EnvBase as _, Symbol as SymbolVal, SymbolSmall},
ConversionError, Env, TryFromVal, TryIntoVal, Val,
};
#[cfg(not(target_family = "wasm"))]
use super::env::SymbolStr;
#[cfg(not(target_family = "wasm"))]
use crate::env::internal::xdr::{ScSymbol, ScVal};
use crate::{
env::MaybeEnv,
unwrap::{UnwrapInfallible, UnwrapOptimized},
};
#[derive(Clone)]
pub struct Symbol {
env: MaybeEnv,
val: SymbolVal,
}
impl Debug for Symbol {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
#[cfg(target_family = "wasm")]
write!(f, "Symbol(..)")?;
#[cfg(not(target_family = "wasm"))]
write!(f, "Symbol({})", self.to_string())?;
Ok(())
}
}
impl Eq for Symbol {}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
self.partial_cmp(other) == Some(Ordering::Equal)
}
}
impl PartialOrd for Symbol {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(Ord::cmp(self, other))
}
}
impl Ord for Symbol {
fn cmp(&self, other: &Self) -> Ordering {
let self_raw = self.val.to_val();
let other_raw = other.val.to_val();
match (
SymbolSmall::try_from(self_raw),
SymbolSmall::try_from(other_raw),
) {
(Ok(self_sym), Ok(other_sym)) => self_sym.cmp(&other_sym),
_ => {
let env: Option<Env> =
match (self.env.clone().try_into(), other.env.clone().try_into()) {
(Err(_), Err(_)) => None,
(Err(_), Ok(e)) => Some(e),
(Ok(e), Err(_)) => Some(e),
(Ok(e1), Ok(e2)) => {
e1.check_same_env(&e2).unwrap_infallible();
Some(e1)
}
};
if let Some(env) = env {
let v = env.obj_cmp(self_raw, other_raw).unwrap_infallible();
v.cmp(&0)
} else {
panic!("symbol object is missing the env reference");
}
}
}
}
}
impl TryFromVal<Env, SymbolVal> for Symbol {
type Error = Infallible;
fn try_from_val(env: &Env, val: &SymbolVal) -> Result<Self, Self::Error> {
Ok(unsafe { Symbol::unchecked_new(env.clone(), *val) })
}
}
impl TryFromVal<Env, Val> for Symbol {
type Error = ConversionError;
fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
Ok(SymbolVal::try_from_val(env, val)?
.try_into_val(env)
.unwrap_infallible())
}
}
impl TryFromVal<Env, Symbol> for Val {
type Error = ConversionError;
fn try_from_val(_env: &Env, v: &Symbol) -> Result<Self, Self::Error> {
Ok(v.to_val())
}
}
impl TryFromVal<Env, &Symbol> for Val {
type Error = ConversionError;
fn try_from_val(_env: &Env, v: &&Symbol) -> Result<Self, Self::Error> {
Ok(v.to_val())
}
}
impl TryFromVal<Env, &str> for Symbol {
type Error = ConversionError;
fn try_from_val(env: &Env, val: &&str) -> Result<Self, Self::Error> {
Ok(SymbolVal::try_from_val(env, val)?
.try_into_val(env)
.unwrap_infallible())
}
}
#[cfg(not(target_family = "wasm"))]
impl TryFrom<&Symbol> for ScVal {
type Error = ConversionError;
fn try_from(v: &Symbol) -> Result<Self, ConversionError> {
if let Ok(ss) = SymbolSmall::try_from(v.val) {
Ok(ScVal::try_from(ss)?)
} else {
let e: Env = v.env.clone().try_into()?;
ScVal::try_from_val(&e, &v.to_val())
}
}
}
#[cfg(not(target_family = "wasm"))]
impl TryFrom<Symbol> for ScVal {
type Error = ConversionError;
fn try_from(v: Symbol) -> Result<Self, ConversionError> {
(&v).try_into()
}
}
#[cfg(not(target_family = "wasm"))]
impl TryFromVal<Env, Symbol> for ScVal {
type Error = ConversionError;
fn try_from_val(_e: &Env, v: &Symbol) -> Result<Self, ConversionError> {
v.try_into()
}
}
#[cfg(not(target_family = "wasm"))]
impl TryFromVal<Env, ScVal> for Symbol {
type Error = ConversionError;
fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
Ok(SymbolVal::try_from_val(env, &Val::try_from_val(env, val)?)?
.try_into_val(env)
.unwrap_infallible())
}
}
#[cfg(not(target_family = "wasm"))]
impl TryFromVal<Env, ScSymbol> for Symbol {
type Error = ConversionError;
fn try_from_val(env: &Env, val: &ScSymbol) -> Result<Self, Self::Error> {
Ok(SymbolVal::try_from_val(env, val)?
.try_into_val(env)
.unwrap_infallible())
}
}
impl Symbol {
pub fn new(env: &Env, s: &str) -> Self {
Self {
env: env.clone().into(),
val: s.try_into_val(env).unwrap_optimized(),
}
}
#[doc(hidden)]
#[deprecated(note = "use [symbol_short!()]")]
pub const fn short(s: &str) -> Self {
if let Ok(sym) = SymbolSmall::try_from_str(s) {
Symbol {
env: MaybeEnv::none(),
val: SymbolVal::from_small(sym),
}
} else {
panic!("short symbols are limited to 9 characters");
}
}
#[inline(always)]
pub(crate) unsafe fn unchecked_new(env: Env, val: SymbolVal) -> Self {
Self {
env: env.into(),
val,
}
}
pub fn as_val(&self) -> &Val {
self.val.as_val()
}
pub fn to_val(&self) -> Val {
self.val.to_val()
}
pub fn to_symbol_val(&self) -> SymbolVal {
self.val
}
}
#[cfg(not(target_family = "wasm"))]
extern crate std;
#[cfg(not(target_family = "wasm"))]
impl ToString for Symbol {
fn to_string(&self) -> String {
if let Ok(s) = SymbolSmall::try_from(self.val) {
s.to_string()
} else {
let e: Env = self.env.clone().try_into().unwrap_optimized();
SymbolStr::try_from_val(&e, &self.val)
.unwrap_optimized()
.to_string()
}
}
}