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