use std::fmt::{self, Display, Formatter};
use serde::{Deserialize, Serialize};
use crate::{SymbolId, SymbolPath};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct SymbolRef {
pub id: SymbolId,
pub path: SymbolPath,
}
impl SymbolRef {
pub fn new(id: SymbolId, path: SymbolPath) -> Self {
Self { id, path }
}
#[inline]
pub fn id(&self) -> SymbolId {
self.id
}
#[inline]
pub fn path(&self) -> &SymbolPath {
&self.path
}
#[inline]
pub fn name(&self) -> &str {
self.path.name()
}
#[inline]
pub fn crate_name(&self) -> &str {
self.path.crate_name()
}
}
impl Display for SymbolRef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?}@{}", self.id, self.path)
}
}
impl SymbolRef {
pub fn compact(&self) -> String {
let id_debug = format!("{:?}", self.id);
let inner = id_debug
.strip_prefix("SymbolId(")
.and_then(|s| s.strip_suffix(')'))
.unwrap_or(&id_debug);
format!("{}@{}", inner, self.path)
}
}
impl Serialize for SymbolRef {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(&self.to_string())
}
}
impl<'de> Deserialize<'de> for SymbolRef {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Self::parse(&s).map_err(serde::de::Error::custom)
}
}
impl SymbolRef {
pub fn parse(s: &str) -> Result<Self, String> {
let (id_part, path_part) = s
.split_once('@')
.ok_or_else(|| format!("Invalid SymbolRef format: missing '@' separator in '{}'", s))?;
let id =
SymbolId::parse(id_part).ok_or_else(|| format!("Invalid SymbolId: '{}'", id_part))?;
let path = SymbolPath::parse(path_part)
.map_err(|e| format!("Invalid SymbolPath '{}': {:?}", path_part, e))?;
Ok(Self { id, path })
}
}
#[cfg(test)]
mod tests {
use super::*;
use slotmap::SlotMap;
fn create_test_id() -> SymbolId {
let mut map: SlotMap<SymbolId, &str> = SlotMap::with_key();
map.insert("test")
}
#[test]
fn test_display() {
let id = create_test_id();
let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
let sym_ref = SymbolRef::new(id, path);
let display = sym_ref.to_string();
assert!(display.starts_with("SymbolId("));
assert!(display.contains("@my_crate::MyStruct"));
}
#[test]
fn test_compact() {
let id = create_test_id();
let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
let sym_ref = SymbolRef::new(id, path);
let compact = sym_ref.compact();
assert!(!compact.starts_with("SymbolId("));
assert!(compact.contains("@my_crate::MyStruct"));
}
#[test]
fn test_parse_standard() {
let id = create_test_id();
let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
let sym_ref = SymbolRef::new(id, path.clone());
let display = sym_ref.to_string();
let parsed = SymbolRef::parse(&display).unwrap();
assert_eq!(parsed.id, id);
assert_eq!(parsed.path, path);
}
#[test]
fn test_parse_compact() {
let id = create_test_id();
let path = SymbolPath::parse("my_crate::MyStruct").unwrap();
let sym_ref = SymbolRef::new(id, path.clone());
let compact = sym_ref.compact();
let parsed = SymbolRef::parse(&compact).unwrap();
assert_eq!(parsed.id, id);
assert_eq!(parsed.path, path);
}
#[test]
fn test_serde_roundtrip() {
let id = create_test_id();
let path = SymbolPath::parse("my_crate::foo::Bar").unwrap();
let sym_ref = SymbolRef::new(id, path);
let json = serde_json::to_string(&sym_ref).unwrap();
let parsed: SymbolRef = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.id, sym_ref.id);
assert_eq!(parsed.path, sym_ref.path);
}
#[test]
fn test_accessors() {
let id = create_test_id();
let path = SymbolPath::parse("my_crate::foo::Bar").unwrap();
let sym_ref = SymbolRef::new(id, path);
assert_eq!(sym_ref.id(), id);
assert_eq!(sym_ref.name(), "Bar");
assert_eq!(sym_ref.crate_name(), "my_crate");
}
}