1use crate::ion_data::{IonEq, IonOrd};
2use crate::result::decoding_error;
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)]
12enum SymbolText {
13 Shared(Arc<str>),
15 Owned(String),
17 Unknown,
19}
20
21impl SymbolText {
22 fn text(&self) -> Option<&str> {
23 let text = match self {
24 SymbolText::Shared(s) => s.as_ref(),
25 SymbolText::Owned(s) => s.as_str(),
26 SymbolText::Unknown => return None,
27 };
28 Some(text)
29 }
30}
31
32impl Hash for SymbolText {
33 fn hash<H: Hasher>(&self, state: &mut H) {
34 match self {
35 SymbolText::Shared(text) => text.hash(state),
36 SymbolText::Owned(text) => text.hash(state),
37 SymbolText::Unknown => 0.hash(state),
38 }
39 }
40}
41
42impl Clone for SymbolText {
43 fn clone(&self) -> Self {
44 match self {
45 SymbolText::Owned(text) => SymbolText::Owned(text.to_owned()),
46 SymbolText::Shared(text) => SymbolText::Shared(Arc::clone(text)),
47 SymbolText::Unknown => SymbolText::Unknown,
48 }
49 }
50}
51
52impl PartialEq<Self> for SymbolText {
53 fn eq(&self, other: &Self) -> bool {
54 self.cmp(other) == Ordering::Equal
55 }
56}
57
58impl PartialOrd<Self> for SymbolText {
59 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
60 Some(self.cmp(other))
61 }
62}
63
64impl Ord for SymbolText {
65 fn cmp(&self, other: &Self) -> Ordering {
66 match (self.text(), other.text()) {
67 (Some(s1), Some(s2)) => s1.cmp(s2),
69 (Some(_), None) => Ordering::Greater,
71 (None, Some(_)) => Ordering::Less,
72 (None, None) => Ordering::Equal,
73 }
74 }
75}
76
77#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
81pub struct Symbol {
82 text: SymbolText,
83}
84
85impl Symbol {
86 pub fn owned<I: Into<String>>(text: I) -> Symbol {
87 Symbol {
88 text: SymbolText::Owned(text.into()),
89 }
90 }
91
92 pub fn shared(text: Arc<str>) -> Symbol {
93 Symbol {
94 text: SymbolText::Shared(text),
95 }
96 }
97
98 pub fn unknown_text() -> Symbol {
99 Symbol {
100 text: SymbolText::Unknown,
101 }
102 }
103
104 pub(crate) fn into_shared(self) -> Symbol {
106 match self.text {
107 SymbolText::Shared(text) => Symbol::shared(text),
108 SymbolText::Owned(text) => Symbol::shared(text.into()),
109 SymbolText::Unknown => Symbol::unknown_text(),
110 }
111 }
112
113 pub fn text(&self) -> Option<&str> {
114 self.text.text()
115 }
116
117 pub fn text_or_error(&self) -> IonResult<&str> {
118 match self.text() {
119 Some(text) => Ok(text),
120 None => decoding_error("symbol has unknown text"),
121 }
122 }
123}
124
125impl IonEq for Symbol {
126 fn ion_eq(&self, other: &Self) -> bool {
127 self == other
128 }
129}
130
131impl IonOrd for Symbol {
132 fn ion_cmp(&self, other: &Self) -> Ordering {
133 self.cmp(other)
134 }
135}
136
137impl From<&str> for Symbol {
142 fn from(text: &str) -> Self {
143 Symbol::owned(text)
144 }
145}
146
147impl From<String> for Symbol {
148 fn from(text: String) -> Self {
149 Symbol::owned(text)
150 }
151}
152
153impl From<&String> for Symbol {
154 fn from(text: &String) -> Self {
155 text.as_str().into()
156 }
157}
158
159impl<'a> From<&'a Symbol> for Symbol {
160 fn from(text: &'a Symbol) -> Self {
161 text.clone()
162 }
163}
164
165impl<'a> From<SymbolRef<'a>> for Symbol {
166 fn from(symbol_ref: SymbolRef<'a>) -> Self {
167 symbol_ref.to_owned()
168 }
169}
170
171impl Display for Symbol {
172 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
173 match self.text() {
174 None => write!(f, "$0"),
175 Some(text) => write!(f, "'{text}'"),
176 }
177 }
178}
179
180impl<A: AsRef<str>> PartialEq<A> for Symbol {
181 fn eq(&self, other: &A) -> bool {
182 self.text()
183 .map(|t| t == other.as_ref())
185 .unwrap_or(false)
187 }
188}
189
190impl PartialEq<Symbol> for String {
191 fn eq(&self, other: &Symbol) -> bool {
192 other.text().map(|t| t == self.as_str()).unwrap_or(false)
193 }
194}
195
196impl PartialEq<Symbol> for &str {
197 fn eq(&self, other: &Symbol) -> bool {
198 other.text().map(|t| &t == self).unwrap_or(false)
199 }
200}
201
202impl Borrow<str> for Symbol {
205 fn borrow(&self) -> &str {
206 self.text()
207 .expect("cannot borrow a &str from a Symbol with unknown text")
208 }
209}
210
211#[cfg(test)]
212mod symbol_tests {
213 use super::*;
214
215 #[test]
216 fn ordering_and_eq() {
217 let mut symbols = vec![
218 Symbol::owned("foo"),
219 Symbol::shared(Arc::from("bar")),
220 Symbol::shared(Arc::from("baz")),
221 Symbol::owned("quux"),
222 ];
223 symbols.as_mut_slice().sort();
225 let expected = vec![
229 Symbol::owned("bar"),
230 Symbol::owned("baz"),
231 Symbol::owned("foo"),
232 Symbol::owned("quux"),
233 ];
234 assert_eq!(symbols, expected)
235 }
236}