1use core::{cmp::Ordering, convert::Infallible, fmt::Debug};
2
3use super::{
4 env::internal::{Env as _, Symbol as SymbolVal, SymbolSmall},
5 ConversionError, Env, TryFromVal, TryIntoVal, Val,
6};
7
8#[cfg(not(target_family = "wasm"))]
9use super::env::SymbolStr;
10
11#[cfg(not(target_family = "wasm"))]
12use crate::env::internal::xdr::{ScSymbol, ScVal};
13use crate::{
14 env::MaybeEnv,
15 unwrap::{UnwrapInfallible, UnwrapOptimized},
16};
17
18#[derive(Clone)]
30pub struct Symbol {
31 env: MaybeEnv,
32 val: SymbolVal,
33}
34
35impl Debug for Symbol {
36 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
37 #[cfg(target_family = "wasm")]
38 write!(f, "Symbol(..)")?;
39 #[cfg(not(target_family = "wasm"))]
40 write!(f, "Symbol({})", self.to_string())?;
41 Ok(())
42 }
43}
44
45impl Eq for Symbol {}
46
47impl PartialEq for Symbol {
48 fn eq(&self, other: &Self) -> bool {
49 self.partial_cmp(other) == Some(Ordering::Equal)
50 }
51}
52
53impl PartialOrd for Symbol {
54 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
55 Some(Ord::cmp(self, other))
56 }
57}
58
59impl Ord for Symbol {
60 fn cmp(&self, other: &Self) -> Ordering {
61 let self_raw = self.val.to_val();
62 let other_raw = other.val.to_val();
63
64 match (
65 SymbolSmall::try_from(self_raw),
66 SymbolSmall::try_from(other_raw),
67 ) {
68 (Ok(self_sym), Ok(other_sym)) => self_sym.cmp(&other_sym),
70 _ => {
73 let (e1, e2): (Result<Env, _>, Result<Env, _>) =
74 (self.env.clone().try_into(), other.env.clone().try_into());
75 match (e1, e2) {
76 (Err(_), Err(_)) => {
77 panic!("symbol object is missing the env reference");
78 }
79 (Err(_), Ok(e)) | (Ok(e), Err(_)) => {
80 let v = e.obj_cmp(self_raw, other_raw).unwrap_infallible();
81 v.cmp(&0)
82 }
83 #[cfg(not(target_family = "wasm"))]
84 (Ok(e1), Ok(e2)) => {
85 if !e1.is_same_env(&e2) {
86 return ScVal::from(self).cmp(&ScVal::from(other));
87 }
88 let v = e1.obj_cmp(self_raw, other_raw).unwrap_infallible();
89 v.cmp(&0)
90 }
91 #[cfg(target_family = "wasm")]
92 (Ok(e), Ok(_)) => {
93 let v = e.obj_cmp(self_raw, other_raw).unwrap_infallible();
94 v.cmp(&0)
95 }
96 }
97 }
98 }
99 }
100}
101
102impl TryFromVal<Env, SymbolVal> for Symbol {
103 type Error = Infallible;
104
105 fn try_from_val(env: &Env, val: &SymbolVal) -> Result<Self, Self::Error> {
106 Ok(unsafe { Symbol::unchecked_new(env.clone(), *val) })
107 }
108}
109
110impl TryFromVal<Env, Val> for Symbol {
111 type Error = ConversionError;
112
113 fn try_from_val(env: &Env, val: &Val) -> Result<Self, Self::Error> {
114 Ok(SymbolVal::try_from_val(env, val)?
115 .try_into_val(env)
116 .unwrap_infallible())
117 }
118}
119
120impl TryFromVal<Env, Symbol> for Val {
121 type Error = ConversionError;
122
123 fn try_from_val(_env: &Env, v: &Symbol) -> Result<Self, Self::Error> {
124 Ok(v.to_val())
125 }
126}
127
128impl TryFromVal<Env, &Symbol> for Val {
129 type Error = ConversionError;
130
131 fn try_from_val(_env: &Env, v: &&Symbol) -> Result<Self, Self::Error> {
132 Ok(v.to_val())
133 }
134}
135
136impl TryFromVal<Env, &str> for Symbol {
137 type Error = ConversionError;
138
139 fn try_from_val(env: &Env, val: &&str) -> Result<Self, Self::Error> {
140 Ok(SymbolVal::try_from_val(env, val)?
141 .try_into_val(env)
142 .unwrap_infallible())
143 }
144}
145
146#[cfg(not(target_family = "wasm"))]
147impl From<&Symbol> for ScVal {
148 fn from(v: &Symbol) -> Self {
149 if let Ok(ss) = SymbolSmall::try_from(v.val) {
155 ScVal::try_from(ss).unwrap()
156 } else {
157 let e: Env = v.env.clone().try_into().unwrap();
158 ScVal::try_from_val(&e, &v.to_val()).unwrap()
159 }
160 }
161}
162
163#[cfg(not(target_family = "wasm"))]
164impl From<Symbol> for ScVal {
165 fn from(v: Symbol) -> Self {
166 (&v).into()
167 }
168}
169
170#[cfg(not(target_family = "wasm"))]
171impl TryFromVal<Env, Symbol> for ScVal {
172 type Error = ConversionError;
173 fn try_from_val(_e: &Env, v: &Symbol) -> Result<Self, ConversionError> {
174 Ok(v.into())
175 }
176}
177
178#[cfg(not(target_family = "wasm"))]
179impl TryFromVal<Env, ScVal> for Symbol {
180 type Error = ConversionError;
181 fn try_from_val(env: &Env, val: &ScVal) -> Result<Self, Self::Error> {
182 Ok(SymbolVal::try_from_val(env, &Val::try_from_val(env, val)?)?
183 .try_into_val(env)
184 .unwrap_infallible())
185 }
186}
187
188#[cfg(not(target_family = "wasm"))]
189impl TryFromVal<Env, ScSymbol> for Symbol {
190 type Error = ConversionError;
191 fn try_from_val(env: &Env, val: &ScSymbol) -> Result<Self, Self::Error> {
192 Ok(SymbolVal::try_from_val(env, val)?
193 .try_into_val(env)
194 .unwrap_infallible())
195 }
196}
197
198impl Symbol {
199 pub fn new(env: &Env, s: &str) -> Self {
213 Self {
214 env: env.clone().into(),
215 val: s.try_into_val(env).unwrap_optimized(),
216 }
217 }
218
219 #[doc(hidden)]
252 #[deprecated(note = "use [symbol_short!()]")]
253 pub const fn short(s: &str) -> Self {
254 if let Ok(sym) = SymbolSmall::try_from_str(s) {
255 Symbol {
256 env: MaybeEnv::none(),
257 val: SymbolVal::from_small(sym),
258 }
259 } else {
260 panic!("short symbols are limited to 9 characters");
261 }
262 }
263
264 #[inline(always)]
265 pub(crate) unsafe fn unchecked_new(env: Env, val: SymbolVal) -> Self {
266 Self {
267 env: env.into(),
268 val,
269 }
270 }
271
272 pub fn as_val(&self) -> &Val {
273 self.val.as_val()
274 }
275
276 pub fn to_val(&self) -> Val {
277 self.val.to_val()
278 }
279
280 pub fn to_symbol_val(&self) -> SymbolVal {
281 self.val
282 }
283}
284
285#[cfg(not(target_family = "wasm"))]
286extern crate std;
287#[cfg(not(target_family = "wasm"))]
288impl ToString for Symbol {
289 fn to_string(&self) -> String {
290 if let Ok(s) = SymbolSmall::try_from(self.val) {
291 s.to_string()
292 } else {
293 let e: Env = self.env.clone().try_into().unwrap_optimized();
294 SymbolStr::try_from_val(&e, &self.val)
295 .unwrap_optimized()
296 .to_string()
297 }
298 }
299}