1use crate::raw_symbol_ref::{AsRawSymbolRef, RawSymbolRef};
2use crate::result::IonFailure;
3use crate::{IonResult, Str, Symbol};
4use std::borrow::Borrow;
5use std::fmt::{Debug, Formatter};
6use std::hash::{Hash, Hasher};
7
8#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)]
11pub struct SymbolRef<'a> {
12 text: Option<&'a str>,
13}
14
15impl Debug for SymbolRef<'_> {
16 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
17 write!(f, "{}", self.text().unwrap_or("$0"))
18 }
19}
20
21impl<'a> SymbolRef<'a> {
22 pub fn text(&self) -> Option<&'a str> {
24 self.text
25 }
26
27 pub fn with_unknown_text() -> Self {
29 SymbolRef { text: None }
30 }
31
32 pub fn with_text(text: &'a str) -> SymbolRef<'a> {
34 SymbolRef { text: Some(text) }
35 }
36
37 pub fn to_owned(self) -> Symbol {
38 match self.text {
39 None => Symbol::unknown_text(),
40 Some(text) => Symbol::owned(Str::from(text)),
41 }
42 }
43
44 pub fn expect_text(&self) -> IonResult<&'a str> {
45 match self.text() {
46 Some(text) => Ok(text),
47 None => IonResult::decoding_error("symbol has unknown text"),
48 }
49 }
50}
51
52impl<A> PartialEq<A> for SymbolRef<'_>
53where
54 A: AsSymbolRef,
55{
56 fn eq(&self, other: &A) -> bool {
57 let other_symbol_ref = other.as_symbol_ref();
58 self == &other_symbol_ref
59 }
60}
61
62pub trait AsSymbolRef {
65 fn as_symbol_ref(&self) -> SymbolRef<'_>;
66}
67
68impl<A: AsRef<str>> AsSymbolRef for A {
70 fn as_symbol_ref(&self) -> SymbolRef<'_> {
71 SymbolRef {
72 text: Some(self.as_ref()),
73 }
74 }
75}
76
77impl Hash for SymbolRef<'_> {
78 fn hash<H: Hasher>(&self, state: &mut H) {
79 match self.text() {
80 None => 0.hash(state),
81 Some(text) => text.hash(state),
82 }
83 }
84}
85
86impl<'a> From<&'a str> for SymbolRef<'a> {
87 fn from(text: &'a str) -> Self {
88 Self { text: Some(text) }
89 }
90}
91
92impl<'a> From<&'a Symbol> for SymbolRef<'a> {
93 fn from(symbol: &'a Symbol) -> Self {
94 Self {
95 text: symbol.text(),
96 }
97 }
98}
99
100impl Borrow<str> for SymbolRef<'_> {
103 fn borrow(&self) -> &str {
104 self.text()
105 .expect("cannot borrow a &str from a SymbolRef with unknown text")
106 }
107}
108
109impl AsSymbolRef for Symbol {
112 fn as_symbol_ref(&self) -> SymbolRef<'_> {
113 self.text()
114 .map(SymbolRef::with_text)
115 .unwrap_or_else(SymbolRef::with_unknown_text)
116 }
117}
118
119impl AsSymbolRef for &Symbol {
120 fn as_symbol_ref(&self) -> SymbolRef<'_> {
121 self.text()
122 .map(SymbolRef::with_text)
123 .unwrap_or_else(SymbolRef::with_unknown_text)
124 }
125}
126
127impl AsRawSymbolRef for SymbolRef<'_> {
128 fn as_raw_symbol_ref(&self) -> RawSymbolRef<'_> {
129 match &self.text {
130 None => RawSymbolRef::SymbolId(0),
131 Some(text) => RawSymbolRef::Text(text),
132 }
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 #[test]
141 fn symbol_ref_with_text() {
142 let symbol_ref = SymbolRef::with_text("foo");
143 assert_eq!(Some("foo"), symbol_ref.text());
144 }
145
146 #[test]
147 fn symbol_ref_with_unknown_text() {
148 let symbol_ref = SymbolRef::with_unknown_text();
149 assert_eq!(None, symbol_ref.text());
150 }
151
152 #[test]
153 fn str_as_symbol_ref() {
154 let symbol_ref: SymbolRef<'_> = "foo".as_symbol_ref();
155 assert_eq!(Some("foo"), symbol_ref.text());
156 }
157
158 #[test]
159 fn symbol_as_symbol_ref() {
160 let symbol = Symbol::owned("foo");
161 let symbol_ref: SymbolRef<'_> = symbol.as_symbol_ref();
162 assert_eq!(Some("foo"), symbol_ref.text());
163 }
164
165 #[test]
166 fn symbol_with_unknown_text_as_symbol_ref() {
167 let symbol = Symbol::unknown_text();
168 let symbol_ref: SymbolRef<'_> = symbol.as_symbol_ref();
169 assert_eq!(None, symbol_ref.text());
170 }
171}