Skip to main content

java_bin_name/
class.rs

1use core::{convert::Infallible, fmt::Display};
2
3use alloc::boxed::Box;
4
5use crate::{Cursor, Parse, ReprForm, method::MethodDescriptor, strip_digits_prefix};
6
7/// Binary name of a class or interface.
8///
9/// See [JLS 13.1](https://docs.oracle.com/javase/specs/jls/se25/html/jls-13.html#jls-13.1).
10#[derive(Debug, Clone, PartialEq, Eq, Hash)]
11pub enum ClassName<'a> {
12    /// Binary name of a top level class or interface.
13    TopLevel(CanonicalClassName<'a>),
14    /// Binary name of a member class or interface, or
15    /// a type variable declared by a generic class or interface.
16    #[doc(alias = "Generic")]
17    Member {
18        /// The binary name of its immediately enclosing class or interface.
19        parent: Box<Self>,
20        /// The simple name of the member or the type variable.
21        simple: &'a str,
22    },
23    /// Binary name of a local class or interface.
24    Local {
25        /// The binary name of its immediately enclosing class or interface.
26        parent: Box<Self>,
27        /// The simple name of the local class.
28        simple: &'a str,
29        /// A non-empty sequence of digits.
30        index: u32,
31    },
32    /// Binary name of an anonymous class.
33    Anonymous {
34        /// The binary name of its immediately enclosing class or interface.
35        parent: Box<Self>,
36        /// A non-empty sequence of digits.
37        index: u32,
38    },
39    /// Binary name of a type variable declared by a generic method, or
40    /// a constructor.
41    #[doc(alias = "ConstructorGeneric")]
42    MethodGeneric {
43        /// The binary name of the class or interface declaring the method or constructor.
44        class: Box<Self>,
45        /// The descriptor of the method or constructor.
46        method: MethodDescriptor<'a>,
47        /// The simple name of the type variable.
48        simple: &'a str,
49    },
50}
51
52impl Display for ClassName<'_> {
53    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
54        match self {
55            ClassName::TopLevel(canonical_class_name) => write!(f, "{canonical_class_name}"),
56            ClassName::Member { parent, simple } => write!(f, "{parent}${simple}"),
57            ClassName::Local {
58                parent,
59                simple,
60                index,
61            } => write!(f, "{parent}${index}{simple}"),
62            ClassName::Anonymous { parent, index } => write!(f, "{parent}${index}"),
63            ClassName::MethodGeneric {
64                class,
65                method,
66                simple,
67            } => write!(f, "{class}${method}${simple}"),
68        }
69    }
70}
71
72impl<'a> Parse<'a> for ClassName<'a> {
73    type Error = Infallible;
74
75    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
76        let s = cursor.get();
77        if let Some((parent, simple)) = s.rsplit_once('$') {
78            if let Some((parent, method)) = parent.rsplit_once('$')
79                && method.chars().next().is_some_and(|c| c == '(')
80                && let Ok(method) = MethodDescriptor::parse_from(&mut Cursor::new(method))
81            {
82                return Ok(Self::MethodGeneric {
83                    class: Box::new(Self::parse_from(&mut Cursor::new(parent)).unwrap()),
84                    method,
85                    simple,
86                });
87            }
88
89            let parent = Box::new(Self::parse_from(&mut Cursor::new(parent))?);
90            let (digits, simple) = strip_digits_prefix(simple);
91            cursor.clear();
92            Ok(match (digits, simple.is_empty()) {
93                // expected non-empty, but have to handle errors that way
94                (None, _) => Self::Member { parent, simple },
95                (Some(index), true) => Self::Anonymous { parent, index },
96                (Some(index), false) => Self::Local {
97                    parent,
98                    simple,
99                    index,
100                },
101            })
102        } else {
103            CanonicalClassName::parse_from(cursor).map(Self::TopLevel)
104        }
105    }
106}
107
108/// Canonical, or fully qualified name of a class or interface.
109///
110/// See [JLS 6.7](https://docs.oracle.com/javase/specs/jls/se25/html/jls-6.html#jls-6.7).
111#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
112pub struct CanonicalClassName<'a> {
113    /// The fully qualified name of the package.
114    pub package: Option<&'a str>,
115    /// The simple name of the class or interface.
116    pub simple: &'a str,
117    /// The representation form of this class name.
118    pub form: ReprForm,
119}
120
121impl Display for CanonicalClassName<'_> {
122    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
123        if let Some(pkg) = self.package {
124            write!(f, "{pkg}{}{}", self.form.package_separator(), self.simple)
125        } else {
126            write!(f, "{}", self.simple)
127        }
128    }
129}
130
131impl<'a> Parse<'a> for CanonicalClassName<'a> {
132    type Error = Infallible;
133
134    fn parse_from(cursor: &mut Cursor<'a>) -> Result<Self, Self::Error> {
135        Ok(cursor.advance(|s| {
136            let form = if s.contains('/') {
137                ReprForm::Internal
138            } else {
139                ReprForm::JLS
140            };
141            let (package, c) = s.rsplit_once(form.package_separator()).unzip();
142            (
143                Self {
144                    package,
145                    simple: c.unwrap_or(s),
146                    form,
147                },
148                "",
149            )
150        }))
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use alloc::boxed::Box;
157
158    use crate::{CanonicalClassName, ClassName, ReprForm, parse, validate_rw};
159
160    #[test]
161    fn top_level() {
162        assert_eq!(
163            parse::<'_, ClassName<'_>>("java.lang.String").unwrap(),
164            ClassName::TopLevel(CanonicalClassName {
165                package: Some("java.lang"),
166                simple: "String",
167                form: ReprForm::JLS,
168            })
169        );
170
171        assert_eq!(
172            parse::<'_, ClassName<'_>>("Foo").unwrap(),
173            ClassName::TopLevel(CanonicalClassName {
174                package: None,
175                simple: "Foo",
176                form: ReprForm::JLS,
177            })
178        );
179
180        validate_rw::<'_, ClassName<'_>>("java.lang.String");
181    }
182
183    #[test]
184    fn top_level_jvm() {
185        assert_eq!(
186            parse::<'_, ClassName<'_>>("java/lang/String").unwrap(),
187            ClassName::TopLevel(CanonicalClassName {
188                package: Some("java/lang"),
189                simple: "String",
190                form: ReprForm::Internal
191            })
192        );
193
194        validate_rw::<'_, ClassName<'_>>("java/lang/String");
195    }
196
197    #[test]
198    fn member() {
199        assert_eq!(
200            parse::<'_, ClassName<'_>>("java.util.Map$Entry").unwrap(),
201            ClassName::Member {
202                parent: Box::new(ClassName::TopLevel(CanonicalClassName {
203                    package: Some("java.util"),
204                    simple: "Map",
205                    form: ReprForm::JLS
206                })),
207                simple: "Entry"
208            }
209        );
210
211        validate_rw::<'_, ClassName<'_>>("java.util.Map$Entry");
212    }
213
214    #[test]
215    fn local() {
216        assert_eq!(
217            parse::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass").unwrap(),
218            ClassName::Local {
219                parent: Box::new(ClassName::TopLevel(CanonicalClassName {
220                    package: Some("com.example"),
221                    simple: "OuterClass",
222                    form: ReprForm::JLS
223                })),
224                index: 1,
225                simple: "LocalClass"
226            }
227        );
228
229        validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1LocalClass");
230    }
231
232    #[test]
233    fn anonymous() {
234        assert_eq!(
235            parse::<'_, ClassName<'_>>("com.example.OuterClass$1").unwrap(),
236            ClassName::Anonymous {
237                parent: Box::new(ClassName::TopLevel(CanonicalClassName {
238                    package: Some("com.example"),
239                    simple: "OuterClass",
240                    form: ReprForm::JLS
241                })),
242                index: 1,
243            }
244        );
245
246        validate_rw::<'_, ClassName<'_>>("com.example.OuterClass$1");
247    }
248}