1use std::fmt::{self, Display, Formatter};
16
17use serde::{Deserialize, Serialize};
18
19use crate::{SymbolId, SymbolPath};
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
36pub struct SymbolRef {
37 pub id: SymbolId,
39 pub path: SymbolPath,
41}
42
43impl SymbolRef {
44 pub fn new(id: SymbolId, path: SymbolPath) -> Self {
46 Self { id, path }
47 }
48
49 #[inline]
51 pub fn id(&self) -> SymbolId {
52 self.id
53 }
54
55 #[inline]
57 pub fn path(&self) -> &SymbolPath {
58 &self.path
59 }
60
61 #[inline]
63 pub fn name(&self) -> &str {
64 self.path.name()
65 }
66
67 #[inline]
69 pub fn crate_name(&self) -> &str {
70 self.path.crate_name()
71 }
72}
73
74impl Display for SymbolRef {
75 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
76 write!(f, "{:?}@{}", self.id, self.path)
78 }
79}
80
81impl SymbolRef {
85 pub fn compact(&self) -> String {
89 let id_debug = format!("{:?}", self.id);
90 let inner = id_debug
92 .strip_prefix("SymbolId(")
93 .and_then(|s| s.strip_suffix(')'))
94 .unwrap_or(&id_debug);
95 format!("{}@{}", inner, self.path)
96 }
97}
98
99impl Serialize for SymbolRef {
102 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
103 where
104 S: serde::Serializer,
105 {
106 serializer.serialize_str(&self.to_string())
108 }
109}
110
111impl<'de> Deserialize<'de> for SymbolRef {
112 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113 where
114 D: serde::Deserializer<'de>,
115 {
116 let s = String::deserialize(deserializer)?;
117 Self::parse(&s).map_err(serde::de::Error::custom)
118 }
119}
120
121impl SymbolRef {
122 pub fn parse(s: &str) -> Result<Self, String> {
128 let (id_part, path_part) = s
129 .split_once('@')
130 .ok_or_else(|| format!("Invalid SymbolRef format: missing '@' separator in '{}'", s))?;
131
132 let id =
133 SymbolId::parse(id_part).ok_or_else(|| format!("Invalid SymbolId: '{}'", id_part))?;
134
135 let path = SymbolPath::parse(path_part)
136 .map_err(|e| format!("Invalid SymbolPath '{}': {:?}", path_part, e))?;
137
138 Ok(Self { id, path })
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145 use slotmap::SlotMap;
146
147 fn create_test_id() -> SymbolId {
148 let mut map: SlotMap<SymbolId, &str> = SlotMap::with_key();
149 map.insert("test")
150 }
151
152 #[test]
153 fn test_display() {
154 let id = create_test_id();
155 let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
156 let sym_ref = SymbolRef::new(id, path);
157
158 let display = sym_ref.to_string();
159 assert!(display.starts_with("SymbolId("));
160 assert!(display.contains("@my_crate::MyStruct"));
161 }
162
163 #[test]
164 fn test_compact() {
165 let id = create_test_id();
166 let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
167 let sym_ref = SymbolRef::new(id, path);
168
169 let compact = sym_ref.compact();
170 assert!(!compact.starts_with("SymbolId("));
171 assert!(compact.contains("@my_crate::MyStruct"));
172 }
173
174 #[test]
175 fn test_parse_standard() {
176 let id = create_test_id();
177 let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
178 let sym_ref = SymbolRef::new(id, path.clone());
179
180 let display = sym_ref.to_string();
181 let parsed = SymbolRef::parse(&display).unwrap();
182
183 assert_eq!(parsed.id, id);
184 assert_eq!(parsed.path, path);
185 }
186
187 #[test]
188 fn test_parse_compact() {
189 let id = create_test_id();
190 let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
191 let sym_ref = SymbolRef::new(id, path.clone());
192
193 let compact = sym_ref.compact();
194 let parsed = SymbolRef::parse(&compact).unwrap();
195
196 assert_eq!(parsed.id, id);
197 assert_eq!(parsed.path, path);
198 }
199
200 #[test]
201 fn test_serde_roundtrip() {
202 let id = create_test_id();
203 let path = SymbolPath::parse("my_crate::foo::Bar").unwrap();
204 let sym_ref = SymbolRef::new(id, path);
205
206 let json = serde_json::to_string(&sym_ref).unwrap();
207 let parsed: SymbolRef = serde_json::from_str(&json).unwrap();
208
209 assert_eq!(parsed.id, sym_ref.id);
210 assert_eq!(parsed.path, sym_ref.path);
211 }
212
213 #[test]
214 fn test_accessors() {
215 let id = create_test_id();
216 let path = SymbolPath::parse("my_crate::foo::Bar").unwrap();
217 let sym_ref = SymbolRef::new(id, path);
218
219 assert_eq!(sym_ref.id(), id);
220 assert_eq!(sym_ref.name(), "Bar");
221 assert_eq!(sym_ref.crate_name(), "my_crate");
222 }
223}