#[derive(
Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub struct SymbolId {
pub kind: SymbolKind,
pub path: Vec<String>,
pub disambiguator: u32,
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub enum SymbolKind {
Schema,
Struct,
Enum,
TypeAlias,
Endpoint,
Variant,
Field,
Primitive,
}
pub(crate) fn external_symbol_kind(fqn: &str) -> Option<SymbolKind> {
match fqn {
"std::option::Option" | "std::result::Result" => Some(SymbolKind::Enum),
_ if is_well_known_external_type(fqn) => Some(SymbolKind::Primitive),
_ => None,
}
}
pub(crate) fn is_well_known_external_type(fqn: &str) -> bool {
matches!(
fqn,
"String"
| "bool"
| "char"
| "i8"
| "i16"
| "i32"
| "i64"
| "i128"
| "isize"
| "u8"
| "u16"
| "u32"
| "u64"
| "u128"
| "usize"
| "f32"
| "f64"
) || fqn.starts_with("std::")
|| fqn.starts_with("chrono::")
|| fqn.starts_with("uuid::")
|| fqn.starts_with("url::")
|| fqn.starts_with("serde_json::")
}
impl Default for SymbolId {
fn default() -> Self {
Self {
kind: SymbolKind::Schema,
path: vec!["unknown".to_string()],
disambiguator: 0,
}
}
}
impl SymbolId {
pub fn new(kind: SymbolKind, path: Vec<String>) -> Self {
Self {
kind,
path,
disambiguator: 0,
}
}
pub fn with_disambiguator(kind: SymbolKind, path: Vec<String>, disambiguator: u32) -> Self {
Self {
kind,
path,
disambiguator,
}
}
pub fn is_unknown(&self) -> bool {
self.path.len() == 1 && self.path[0] == "unknown"
}
pub fn struct_id(path: Vec<String>) -> Self {
Self::new(SymbolKind::Struct, path)
}
pub fn name(&self) -> Option<&str> {
self.path.last().map(|s| s.as_str())
}
pub fn qualified_name(&self) -> String {
self.path.join("::")
}
pub fn same_symbol(&self, other: &SymbolId) -> bool {
self.kind == other.kind && self.path == other.path
}
}
impl std::fmt::Display for SymbolId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}({})", self.kind, self.qualified_name())?;
if self.disambiguator > 0 {
write!(f, "#{}", self.disambiguator)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_symbol_id_creation() {
let struct_id = SymbolId::struct_id(vec!["api".to_string(), "User".to_string()]);
assert_eq!(struct_id.kind, SymbolKind::Struct);
assert_eq!(struct_id.path, vec!["api", "User"]);
assert_eq!(struct_id.disambiguator, 0);
assert_eq!(struct_id.name(), Some("User"));
assert_eq!(struct_id.qualified_name(), "api::User");
}
#[test]
fn test_symbol_id_display() {
let struct_id = SymbolId::struct_id(vec!["api".to_string(), "User".to_string()]);
assert_eq!(format!("{struct_id}"), "Struct(api::User)");
let disambiguated = SymbolId::with_disambiguator(
SymbolKind::Struct,
vec!["api".to_string(), "User".to_string()],
1,
);
assert_eq!(format!("{disambiguated}"), "Struct(api::User)#1");
}
#[test]
fn test_same_symbol() {
let id1 = SymbolId::struct_id(vec!["api".to_string(), "User".to_string()]);
let id2 = SymbolId::with_disambiguator(
SymbolKind::Struct,
vec!["api".to_string(), "User".to_string()],
1,
);
assert!(id1.same_symbol(&id2));
}
}