Skip to main content

rajac_base/
qualified_name.rs

1//! Fully qualified class name representation for name resolution.
2
3use crate::shared_string::SharedString;
4use serde::{Deserialize, Serialize};
5use speedy::{Readable, Writable};
6
7pub const UNRESOLVED_PACKAGE: &str = "<unresolved>";
8pub const UNRESOLVED_NAME: &str = "<unresolved>";
9
10/// # What does this represent?
11/// A fully qualified class name represented by a package and a local identifier.
12#[derive(Debug, Clone, PartialEq, Eq, Hash, Readable, Writable, Serialize, Deserialize)]
13pub struct FullyQualifiedClassName {
14    /// The package portion (empty for the default package).
15    package_name: SharedString,
16    /// The local identifier within the package.
17    name: SharedString,
18}
19
20impl FullyQualifiedClassName {
21    /// Creates a new fully qualified class name from a package and local name.
22    pub fn new(package_name: SharedString, name: SharedString) -> Self {
23        Self { package_name, name }
24    }
25
26    /// Creates a fully qualified class name in the default package.
27    pub fn from_ident(name: SharedString) -> Self {
28        Self {
29            package_name: SharedString::empty(),
30            name,
31        }
32    }
33
34    /// Returns the package portion (empty for the default package).
35    pub fn package_name(&self) -> &SharedString {
36        &self.package_name
37    }
38
39    /// Returns the local identifier portion.
40    pub fn name(&self) -> &SharedString {
41        &self.name
42    }
43
44    /// Returns true if this name is in the default package.
45    pub fn is_default_package(&self) -> bool {
46        self.package_name.is_empty()
47    }
48}
49
50impl Default for FullyQualifiedClassName {
51    fn default() -> Self {
52        Self::new(UNRESOLVED_PACKAGE.into(), UNRESOLVED_NAME.into())
53    }
54}
55
56impl std::fmt::Display for FullyQualifiedClassName {
57    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58        if self.package_name.is_empty() {
59            write!(f, "{}", self.name)
60        } else {
61            write!(f, "{}.{}", self.package_name, self.name)
62        }
63    }
64}
65
66impl From<SharedString> for FullyQualifiedClassName {
67    fn from(s: SharedString) -> Self {
68        Self::from_ident(s)
69    }
70}
71
72impl From<&str> for FullyQualifiedClassName {
73    fn from(s: &str) -> Self {
74        Self::from_ident(s.into())
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn from_ident_creates_default_package() {
84        let name = FullyQualifiedClassName::from_ident("foo".into());
85        assert!(name.is_default_package());
86        assert_eq!(name.name().as_str(), "foo");
87    }
88
89    #[test]
90    fn display_joins_package_and_name() {
91        let name = FullyQualifiedClassName::new("java.util".into(), "HashMap".into());
92        assert_eq!(name.to_string(), "java.util.HashMap");
93    }
94
95    #[test]
96    fn display_default_package_is_name_only() {
97        let name = FullyQualifiedClassName::new(SharedString::empty(), "Bar".into());
98        assert_eq!(name.to_string(), "Bar");
99    }
100
101    #[test]
102    fn from_shared_string_uses_default_package() {
103        let name: FullyQualifiedClassName = "foo".into();
104        assert!(name.is_default_package());
105        assert_eq!(name.name().as_str(), "foo");
106    }
107}