Skip to main content

java_lang/
ident.rs

1use std::{
2    fmt,
3    hash::{Hash, Hasher},
4};
5
6use crate::span::Span;
7
8/// A Java identifier.
9///
10/// Similar to `proc_macro2::Ident` in syn, but uses a `String` internally
11/// since we are not in a procedural macro context.
12#[derive(Clone)]
13pub struct Ident {
14    pub name: String,
15    pub span: Span,
16}
17
18impl Ident {
19    /// Create a new identifier.
20    pub fn new(name: String, span: Span) -> Self {
21        Ident { name, span }
22    }
23
24    /// Create a new identifier from a `&str`.
25    pub fn new_str(name: &str, span: Span) -> Self {
26        Ident {
27            name: name.to_string(),
28            span,
29        }
30    }
31
32    /// Get the name of the identifier.
33    pub fn name(&self) -> &str {
34        &self.name
35    }
36
37    /// Get the span of the identifier.
38    pub fn span(&self) -> Span {
39        self.span
40    }
41
42    /// Create a dummy identifier.
43    pub fn dummy(name: &str) -> Self {
44        Ident {
45            name: name.to_string(),
46            span: Span::call_site(),
47        }
48    }
49}
50
51impl PartialEq for Ident {
52    fn eq(&self, other: &Self) -> bool {
53        self.name == other.name
54    }
55}
56
57impl Eq for Ident {}
58
59impl Hash for Ident {
60    fn hash<H: Hasher>(&self, state: &mut H) {
61        self.name.hash(state);
62    }
63}
64
65impl PartialOrd for Ident {
66    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
67        Some(self.cmp(other))
68    }
69}
70
71impl Ord for Ident {
72    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
73        self.name.cmp(&other.name)
74    }
75}
76
77impl fmt::Debug for Ident {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        write!(f, "Ident({:?})", self.name)
80    }
81}
82
83impl fmt::Display for Ident {
84    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85        write!(f, "{}", self.name)
86    }
87}
88
89impl AsRef<str> for Ident {
90    fn as_ref(&self) -> &str {
91        &self.name
92    }
93}
94
95impl std::borrow::Borrow<str> for Ident {
96    fn borrow(&self) -> &str {
97        &self.name
98    }
99}
100
101/// Construct an identifier from a string literal.
102///
103/// # Panics
104/// Panics if the string is not a valid Java identifier.
105#[macro_export]
106macro_rules! format_ident {
107    ($fmt:expr) => {
108        $crate::Ident::new_str($fmt, $crate::span::Span::call_site())
109    };
110    ($fmt:expr, $($arg:tt)*) => {{
111        let s = std::fmt::format(std::format_args!($fmt, $($arg)*));
112        $crate::Ident::new(s, $crate::span::Span::call_site())
113    }};
114}
115
116/// Check if a character can start a Java identifier.
117pub fn is_java_identifier_start(ch: char) -> bool {
118    unicode_xid::UnicodeXID::is_xid_start(ch) || ch == '$' || ch == '_'
119}
120
121/// Check if a character can be part of a Java identifier.
122pub fn is_java_identifier_continue(ch: char) -> bool {
123    unicode_xid::UnicodeXID::is_xid_continue(ch) || ch == '$'
124}
125
126/// Validate that a string is a valid Java identifier.
127#[allow(dead_code)]
128pub fn is_valid_java_identifier(s: &str) -> bool {
129    if s.is_empty() {
130        return false;
131    }
132    let mut chars = s.chars();
133    if !is_java_identifier_start(chars.next().unwrap()) {
134        return false;
135    }
136    chars.all(is_java_identifier_continue)
137}
138
139/// Validate that a string is a valid Java type identifier
140/// (not a contextual keyword like `record`, `sealed`, `var`, `yield`, `permits`).
141#[allow(dead_code)]
142pub fn is_valid_type_identifier(s: &str) -> bool {
143    is_valid_java_identifier(s)
144        && !matches!(s, "record" | "sealed" | "var" | "yield" | "permits" | "_")
145}