1use crate::ion_data::{IonDataHash, IonDataOrd, IonEq};
2use crate::result::IonFailure;
3use crate::{IonResult, SymbolRef};
4use std::borrow::Borrow;
5use std::cmp::Ordering;
6use std::fmt::{Display, Formatter};
7use std::hash::{Hash, Hasher};
8use std::sync::Arc;
9
10#[derive(Debug, Eq)]
12pub(crate) enum SymbolText {
13 Shared(Arc<str>),
15 Owned(String),
20 Static(&'static str),
22 Unknown,
24}
25
26impl SymbolText {
27 fn text(&self) -> Option<&str> {
28 let text = match self {
29 SymbolText::Shared(s) => s.as_ref(),
30 SymbolText::Owned(s) => s.as_str(),
31 SymbolText::Static(s) => s,
32 SymbolText::Unknown => return None,
33 };
34 Some(text)
35 }
36}
37
38impl Hash for SymbolText {
39 fn hash<H: Hasher>(&self, state: &mut H) {
40 let text = self.text().unwrap_or("");
41 text.hash(state)
42 }
43}
44
45impl Clone for SymbolText {
46 fn clone(&self) -> Self {
47 match self {
48 SymbolText::Owned(text) => SymbolText::Owned(text.to_owned()),
49 SymbolText::Shared(text) => SymbolText::Shared(Arc::clone(text)),
50 SymbolText::Static(text) => SymbolText::Static(text),
51 SymbolText::Unknown => SymbolText::Unknown,
52 }
53 }
54}
55
56impl PartialEq<Self> for SymbolText {
57 fn eq(&self, other: &Self) -> bool {
58 self.cmp(other) == Ordering::Equal
59 }
60}
61
62impl PartialOrd<Self> for SymbolText {
63 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
64 Some(self.cmp(other))
65 }
66}
67
68impl Ord for SymbolText {
69 fn cmp(&self, other: &Self) -> Ordering {
70 match (self.text(), other.text()) {
71 (Some(s1), Some(s2)) => s1.cmp(s2),
73 (Some(_), None) => Ordering::Greater,
75 (None, Some(_)) => Ordering::Less,
76 (None, None) => Ordering::Equal,
77 }
78 }
79}
80
81#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
85pub struct Symbol {
86 pub(crate) text: SymbolText,
87}
88
89impl Symbol {
90 pub fn owned<I: Into<String>>(text: I) -> Symbol {
91 Symbol {
92 text: SymbolText::Owned(text.into()),
93 }
94 }
95
96 pub fn shared(text: Arc<str>) -> Symbol {
97 Symbol {
98 text: SymbolText::Shared(text),
99 }
100 }
101
102 pub const fn static_text(text: &'static str) -> Symbol {
103 Symbol {
104 text: SymbolText::Static(text),
105 }
106 }
107
108 pub fn unknown_text() -> Symbol {
109 Symbol {
110 text: SymbolText::Unknown,
111 }
112 }
113
114 pub fn text(&self) -> Option<&str> {
115 self.text.text()
116 }
117
118 pub fn expect_text(&self) -> IonResult<&str> {
119 match self.text() {
120 Some(text) => Ok(text),
121 None => IonResult::decoding_error("symbol has unknown text"),
122 }
123 }
124}
125
126impl IonEq for Symbol {
127 fn ion_eq(&self, other: &Self) -> bool {
128 self == other
129 }
130}
131
132impl IonDataOrd for Symbol {
133 fn ion_cmp(&self, other: &Self) -> Ordering {
134 self.cmp(other)
135 }
136}
137
138impl IonDataHash for Symbol {
139 fn ion_data_hash<H: Hasher>(&self, state: &mut H) {
140 self.hash(state)
141 }
142}
143
144impl From<&str> for Symbol {
149 fn from(text: &str) -> Self {
150 Symbol::owned(text)
151 }
152}
153
154impl From<String> for Symbol {
155 fn from(text: String) -> Self {
156 Symbol::owned(text)
157 }
158}
159
160impl From<&String> for Symbol {
161 fn from(text: &String) -> Self {
162 text.as_str().into()
163 }
164}
165
166impl<'a> From<&'a Symbol> for Symbol {
167 fn from(text: &'a Symbol) -> Self {
168 text.clone()
169 }
170}
171
172impl<'a> From<SymbolRef<'a>> for Symbol {
173 fn from(symbol_ref: SymbolRef<'a>) -> Self {
174 symbol_ref.to_owned()
175 }
176}
177
178impl Display for Symbol {
179 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
180 match self.text() {
181 None => write!(f, "$0"),
182 Some(text) => write!(f, "'{text}'"),
183 }
184 }
185}
186
187impl<A: AsRef<str>> PartialEq<A> for Symbol {
188 fn eq(&self, other: &A) -> bool {
189 self.text()
190 .map(|t| t == other.as_ref())
192 .unwrap_or(false)
194 }
195}
196
197impl PartialEq<Symbol> for String {
198 fn eq(&self, other: &Symbol) -> bool {
199 other.text().map(|t| t == self.as_str()).unwrap_or(false)
200 }
201}
202
203impl PartialEq<Symbol> for &str {
204 fn eq(&self, other: &Symbol) -> bool {
205 other.text().map(|t| &t == self).unwrap_or(false)
206 }
207}
208
209impl Borrow<str> for Symbol {
213 fn borrow(&self) -> &str {
214 self.text().unwrap_or("")
216 }
217}
218
219#[cfg(test)]
220mod symbol_tests {
221 use super::*;
222
223 const TEST_STATIC_TEXT_IS_CONST: Symbol = Symbol::static_text("foo");
225
226 #[test]
227 fn test_static_text_is_const() {
228 assert_eq!(TEST_STATIC_TEXT_IS_CONST, "foo")
230 }
231
232 #[test]
233 fn ordering_and_eq() {
234 let mut symbols = vec![
235 Symbol::owned("foo"),
236 Symbol::shared(Arc::from("bar")),
237 Symbol::shared(Arc::from("baz")),
238 Symbol::owned("quux"),
239 ];
240 symbols.as_mut_slice().sort();
242 let expected = vec![
246 Symbol::owned("bar"),
247 Symbol::owned("baz"),
248 Symbol::owned("foo"),
249 Symbol::owned("quux"),
250 ];
251 assert_eq!(symbols, expected)
252 }
253
254 #[test]
255 fn partial_eq_str() {
256 let symbols = vec![
257 Symbol::shared(Arc::from("bar")),
258 Symbol::shared(Arc::from("baz")),
259 Symbol::owned("foo"),
260 Symbol::owned("quux"),
261 ];
262 let expected = vec!["bar", "baz", "foo", "quux"];
264 assert_eq!(symbols, expected)
265 }
266}